ninja gn
I’m a compiler enthusiast, who has been learning how the V8 JavaScript Engine works. Of course, the best way to learn something is to write about it, so that’s why I’m sharing my experiences here. I hope this might be interesting to others too.
我是一个编译器爱好者,一直在学习 V8 JavaScript Engine的 工作方式。 当然,最好的学习方法是写一些东西,所以这就是我在这里分享经验的原因。 我希望这对其他人也可能很有趣。
This first blog post is an overview of how V8 is compiled. As you can see from the V8 source code repository, the V8 Engine is mostly written in C++, requiring source code to be compiled into executable files. This should be no surprise, given that V8’s primary purpose is fast compilation and execution of JavaScript programs.
第一篇博客文章概述了如何编译V8。 从V8源代码存储库中可以看到,V8 Engine主要是用C ++编写的,要求将源代码编译成可执行文件。 鉴于V8的主要目的是快速编译和执行JavaScript程序,这不足为奇。
I’ll be discussing three main topics related to compiling the V8 executables:
我将讨论与编译V8可执行文件有关的三个主要主题:
The
gm.py
wrapper script, providing a convenient approach to compile V8 from source, and for invoking the test suites.gm.py
包装器脚本,提供了一种从源代码编译V8以及调用测试套件的便捷方法。The GN meta-build system (invoked by
gm.py
) taking an easy-to-read description of the software components, then auto-generating a machine-readable build description suitable for the Ninja build tool.GN元构建系统 (由
gm.py
调用)采用易于理解的软件组件描述,然后自动生成适合Ninja构建工具的机器可读构建描述。Finally, the Ninja build tool uses that same machine-readable build description to analyze inter-file dependencies and invoke the relevant compilers.
最后, Ninja构建工具使用相同的机器可读构建描述来分析文件间的依赖关系并调用相关的编译器。
The earlier diagram (at the top of this blog post) illustrates the overall flow of tool invocation, and shows which files are read, generated, and invoked.
前面的图(在本博客文章的顶部)说明了工具调用的总体流程,并显示了读取 , 生成和调用哪些文件。
Let’s examine each step in detail. If you’re new to this type of compilation process, I’ll put in a shameless plug for this book on Software Build Systems. It’s been almost ten years since I wrote that book, but the underlying concepts remain the same.
让我们详细检查每个步骤。 如果您是这种编译过程的新手,那么我将为这本关于Software Build Systems的书提供一个无耻的插件。 自从我写那本书至今已经快十年了,但基本概念却保持不变。
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/f60cdfb39309f07cc184a3c7f4643725.png)
gm.py脚本 (The gm.py Script)
The first time you compile V8, you should use the recommended gm.py
script to fully compile all the object files, libraries, and executables.
首次编译V8时,应使用推荐的gm.py
脚本完全编译所有目标文件,库和可执行文件。
$ ./tools/dev/gm.py x64.release.check
This is described in the V8 documentation as a convenience script because it’s a one-step solution for all the steps you need to get started. It takes about 20 minutes to run to completion (on my MacBook). Here’s what it’s doing:
V8文档中将其描述为方便脚本,因为它是您入门所需的所有步骤的一步解决方案。 完成操作大约需要20分钟(在我的MacBook上)。 它在做什么:
1.创建和配置构建输出目录 (1. Creating and Configuring the Build Output Directories)
Using the best practice that object and executable files should be stored separately from the source code, the gm.py
script creates the v8/out/x64.release
directory. In this example, we’ve asked for V8 to be compiled for the x64.release
target (Intel x86 64-bit for release images), although if you also want to compile for different targets (such as x64.debug
or arm64.debug
), separate directories would be created for those.
使用最佳实践,即应将目标文件和可执行文件与源代码分开存储, gm.py
脚本创建v8/out/x64.release
目录。 在此示例中,尽管您还希望针对不同的目标(例如x64.debug
或arm64.debug
进行编译,但我们仍要求为x64.release
目标(英特尔x86 64位用于发行映像)编译arm64.debug
),则将为这些目录创建单独的目录。
This step also generates the args.gn
file in the v8/out/x64.release
directory, specifying the build options for this configuration (most notable are the is_debug
and target_cpu
options).
此步骤还将在v8/out/x64.release
目录中生成args.gn
文件,并指定此配置的构建选项(最著名的是is_debug
和target_cpu
选项)。
is_component_build = false
is_debug = false
target_cpu = "x64"
use_goma = false
goma_dir = "None"
v8_enable_backtrace = true
v8_enable_disassembler = true
v8_enable_object_print = true
v8_enable_verify_heap = true
2.根据GN
构建规范自动生成N inja
文件 (2. Auto-Generating Ninja
files from the GN
Build Specification)
The next step in the build process is for gm.py
to invoke the GN tool to translate the human-readable BUILD.gn
file into lower-level files for the Ninja tool (with .ninja
suffix).
生成过程的下一步是gm.py
调用GN工具,将人类可读的BUILD.gn
文件转换为Ninja工具(带有.ninja
后缀)的较低级文件。
The BUILD.gn
file contains easy-to-read directives specifying the content of each build target. In the following example, the d8
executable is constructed from a small number of C++ source files, linked together with additional libraries that contain the core JavaScript engine.
BUILD.gn
文件包含易于阅读的指令,用于指定每个构建目标的内容。 在以下示例中, d8
可执行文件由少量C ++源文件构造而成,并与包含核心JavaScript引擎的其他库链接在一起。
v8_executable("d8") {
sources = [
"src/d8/async-hooks-wrapper.cc",
"src/d8/async-hooks-wrapper.h",
"src/d8/d8-console.cc",
...
"src/d8/d8.cc",
"src/d8/d8.h",
] ... deps = [
":v8",
":v8_libbase",
":v8_libplatform",
...
]
}
Later in this blog post, there’ll be more detail about this file format. For now, let’s look at what happens when the gn gen
command generates all the .ninja
files from the hand-written BUILD.gn
file.
在此博客文章的后面,将有关于此文件格式的更多详细信息。 现在,让我们来看看,当发生了什么gn gen
命令生成的所有.ninja
从手写文件BUILD.gn
文件。
$ gn gen out/x64.release
This results in a collection of roughly 100 .ninja
files in the out/x64.release
directory. Each .ninja
file corresponds to one of the build targets described in the BUILD.gn
file.
这将导致out/x64.release
目录中大约有100个.ninja
文件的集合。 每个.ninja
文件对应的描述构建目标之一BUILD.gn
文件。
./toolchain.ninja
./build.ninja
./obj/d8.ninja
./obj/v8_libbase.ninja
./obj/v8_simple_wasm_compile_fuzzer.ninja
./obj/v8_libplatform.ninja
./obj/v8_simple_multi_return_fuzzer.ninja
...
./obj/test/unittests/cppgc_unittests_sources.ninja
./obj/test/unittests/unittests_sources.ninja
./obj/test/wasm-api-tests/wasm_api_tests.ninja
./obj/test/common_test_headers.ninja
./obj/test/cctest/generate-bytecode-expectations.ninja
./obj/test/cctest/cctest_sources.ninja
./obj/test/cctest/cctest_headers.ninja
./obj/test/cctest/cctest.ninja
As an example, here’s the content of the d8.ninja
file. At first glance, this output is quite similar to an old-style Makefile — that is, not very readable!.
例如,这是d8.ninja
文件的内容。 乍一看,此输出与旧式Makefile非常相似-即可读性不高!
defines = -D_LIBCPP_HAS_NO_ALIGNED_ALLOCATION -DCR_XCODE_VERSION=1160 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D_FORTIFY_SOURCE=2 -D_LIB
CPP_ABI_UNSTABLE -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_ENABLE_NODISCARD -DCR_LIBCXX_REVISION=375504 ...include_dirs = -I../.. -Igen -I../.. -I../../include -Igen -I../../include -Igen/include -I../../third_party/icu/source/common -I../../third_party/icu/source/i18n -I../../include
cflags = -fno-strict-aliasing -fstack-protector -fcolor-diagnostics -fmerge-all-constants -fcrash-diagnostics-dir=../../tools/clang/crashreports -mllvm -instcombine-lower-dbg-declare=0 -fcomplete-member-pointers -arch x86_64 -Wno-builtin-macro-redefined ...build obj/d8/async-hooks-wrapper.o: cxx ../../src/d8/async-hooks-wrapper.cc || obj/d8.inputdeps.stamp
build obj/d8/d8-console.o: cxx ../../src/d8/d8-console.cc || obj/d8.inputdeps.stamp
build obj/d8/d8-js.o: cxx ../../src/d8/d8-js.cc || obj/d8.inputdeps.stamp
build obj/d8/d8-platforms.o: cxx ../../src/d8/d8-platforms.cc || obj/d8.inputdeps.stamp
build obj/d8/d8.o: cxx ../../src/d8/d8.cc || obj/d8.inputdeps.stamp
build obj/d8/d8-posix.o: cxx ../../src/d8/d8-posix.cc || obj/d8.inputdeps.stamp
...
Now that we have all the .ninja
files, we can start to compile the source code.
现在我们有了所有的.ninja
文件,我们可以开始编译源代码了。
3.使用Ninja Build Tool编译对象和可执行文件 (3. Using the Ninja Build Tool to Compile the Objects and Executables)
The next step in the build process is for gm.py
to invoke the C++ compiler (amongst other tools). This is done by invoking the autoninja
command, which itself is a wrapper for the ninja
command.
构建过程的下一步是让gm.py
调用C ++编译器(以及其他工具)。 这是通过调用autoninja
命令完成的,该命令本身是ninja
命令的包装器。
$ autoninja -C out/x64.release d8
This command reads the relevant .ninja
files, determines which object files are missing (or out of date), then invokes the C++ compiler to create them. This process is familiar to anyone who has used the Make build tool (or similar).
该命令读取相关的.ninja
文件,确定缺少(或已过期)的目标文件,然后调用C ++编译器来创建它们。 使用过Make构建工具(或类似工具)的任何人都熟悉此过程。
After roughly 20 minutes (on my MacBook), we end up with a fully populated build tree of roughly 2700 files, including auto-generated source files ( .cc
and .h
suffix), object files (.o
suffix), library files (.a
suffix), and a small number of executable files:
经过大约20分钟的时间(在我的MacBook上),我们得到了大约2700个文件的完全填充的构建树,其中包括自动生成的源文件( .cc
和.h
后缀),目标文件( .o
后缀),库文件( .a
后缀)和少量可执行文件:
out/x64.release/obj
out/x64.release/obj/v8_libbase/time.o
out/x64.release/obj/v8_libbase/semaphore.o
out/x64.release/obj/v8_libbase/platform-macos.o
out/x64.release/obj/v8_libbase/condition-variable.o
out/x64.release/obj/v8_libbase/ieee754.o
out/x64.release/obj/v8_libbase/file-utils.o
...
out/x64.release/obj/v8_compiler/effect-control-linearizer.o
out/x64.release/obj/v8_compiler/js-native-context-specialization.o
out/x64.release/obj/v8_compiler/store-store-elimination.o
out/x64.release/obj/v8_compiler/code-assembler.o
...
out/x64.release/gen/torque-generated/src/wasm/wasm-objects-tq-csa.h
out/x64.release/gen/torque-generated/src/wasm/wasm-objects-tq-csa.cc
out/x64.release/gen/torque-generated/src/objects/map-tq-csa.h
out/x64.release/gen/torque-generated/src/objects/code-tq-csa.h
...
out/x64.release/obj/libv8_libplatform.a
out/x64.release/obj/libwee8.a
out/x64.release/obj/third_party/zlib/libchrome_zlib.a
out/x64.release/obj/third_party/icu/libicui18n.a
out/x64.release/obj/third_party/icu/libicuuc.a
out/x64.release/obj/libv8_libbase.a
...
out/x64.release/obj/d8/d8.o
out/x64.release/obj/d8/d8-posix.o
out/x64.release/obj/d8/d8-console.o
out/x64.release/obj/d8/async-hooks-wrapper.o
out/x64.release/obj/d8/d8-js.o
out/x64.release/obj/d8/d8-platforms.o
out/x64.release/d8
...
Everything is now compiled, so I can run the d8
program to execute some JavaScript code:
现在一切都已编译,因此我可以运行d8
程序来执行一些JavaScript代码:
$ ./out/x64.release/d8
V8 version 8.6.0 (candidate)
d8> console.log(2 + 2);
4
undefined
d8>
Looking at this example output, you might think that d8
is actually the same thing as NodeJS (and the node
command), but it’s actually a simple wrapper around the core V8 libraries. It doesn’t add any of the additional functionality that NodeJS provides, but instead just supports the core JavaScript language. It’s this core library that’s linked into NodeJS, the Chrome browser, and any other software that needs to compile JavaScript.
查看此示例输出,您可能会认为d8
实际上与NodeJS(和node
命令)相同,但实际上是围绕V8核心库的简单包装。 它没有添加NodeJS提供的任何其他功能,而是仅支持核心JavaScript语言。 正是这个核心库链接到NodeJS,Chrome浏览器和任何其他需要编译JavaScript的软件。
4.执行单元测试 (4. Executing the Unit Tests)
The final step of the gm.py
wrapper script is to execute the unit tests. This is done using the run-tests.py
script.
gm.py
包装器脚本的最后一步是执行单元测试。 这是使用run-tests.py
脚本完成的。
$ ./tools/run-tests.py --outdir=out/x64.release \
debugger intl mjsunit cctest message unittests
I plan to talk about V8 testing in another blog post, so I won’t give more detail here. Let’s instead dig deeper into both the GN build tool, and the Ninja build tool.
我计划在另一篇博客文章中讨论V8测试,因此在此不再赘述。 相反,让我们更深入地研究GN构建工具和Ninja构建工具。
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/f60cdfb39309f07cc184a3c7f4643725.png)
GN元构建工具 (The GN Meta Build Tool)
The GN Build Tool is classified as a “meta build” tool in that it doesn’t actually invoke the C++ compiler directly, but instead converts a human-readable build description into a lower-level format suitable for the Ninja build tool. This concept was popularized by CMake, which (amongst other things) is capable of auto-generating a tree ofMakefile
files, to be used by the Make tool
GN Build Tool被归类为“元构建”工具,因为它实际上并不直接调用C ++编译器,而是将人类可读的构建描述转换为适合Ninja构建工具的较低级格式。 这个概念由CMake普及, CMake能够自动生成Makefile
文件树,供Make工具使用。
GN命令行选项 (GN Command Line Options)
We’ve already seen how GN is used (with the gn gen
option) to generate all the .ninja
files, but what else can it do? Here are some interesting examples:
我们已经了解了如何使用GN(带有gn gen
选项)来生成所有.ninja
文件,但是它还能做什么? 以下是一些有趣的示例:
First, we can list all the possible build targets for V8:
首先,我们可以列出V8的所有可能的构建目标:
$ gn ls out/x64.release//:bytecode_builtins_list_generator
//:cppgc
//:cppgc_base
//:cppgc_for_testing
//:cppgc_for_v8_embedders
//:cppgc_standalone
//:d8
//:fuzzer_support
//:gen-regexp-special-case
//:generate_bytecode_builtins_list
//:gn_all
//:json_fuzzer
//:lib_wasm_fuzzer_common
...
Next, we can show all the compilation flags, input files, and dependent libraries for one of these targets:
接下来,我们可以显示这些目标之一的所有编译标志,输入文件和依赖库:
$ gn desc out/x64.release //:d8type: executable
toolchain: //build/toolchain/mac:clang_x64...sources
//src/d8/async-hooks-wrapper.cc
//src/d8/async-hooks-wrapper.h
//src/d8/d8-console.cc
//src/d8/d8-console.h
//src/d8/d8-js.cc
//src/d8/d8-platforms.cc
//src/d8/d8-platforms.h
//src/d8/d8.cc
//src/d8/d8.h
//src/d8/d8-posix.cc
...cflags
-fno-strict-aliasing
-fstack-protector
-fcolor-diagnostics
-fmerge-all-constants
...defines
_LIBCPP_HAS_NO_ALIGNED_ALLOCATION
CR_XCODE_VERSION=1160
CR_CLANG_REVISION="llvmorg-12-init-1771-g1bd7046e-3"
__STDC_CONSTANT_MACROS
__STDC_FORMAT_MACROS
...Direct dependencies
//:v8
//:v8_dump_build_config
//:v8_libbase
//:v8_libplatform
//:v8_tracing
//build/config:executable_deps
//build/win:default_exe_manifest
Finally, the reverse operation is to show which targets will be built from a specific source file.
最后,相反的操作是显示将根据特定的源文件构建哪些目标。
$ gn refs out/x64.release //src/d8/d8-platforms.cc//:d8
//tools/gcmole:v8_run_gcmole
Note that none of these commands actually tell you which targets are currently out of date. As we’ll see later, that’s the responsibility of the Ninja tool.
请注意,这些命令实际上都不会告诉您当前哪些目标已过期。 稍后我们将看到,这是Ninja工具的职责。
了解“ d8”目标的BUILD.gn (Understanding BUILD.gn for the “d8” Target)
The commands shown above are very useful, but how does GN know about the targets and their dependencies? Let’s spend some time looking at the highlights of the v8/BUILD.gn
file. If you want more information on the BUILD.gn
syntax, an excellent introductory presentation is also available.
上面显示的命令非常有用,但是GN如何知道目标及其依赖关系? 让我们花一些时间查看v8/BUILD.gn
文件的v8/BUILD.gn
。 如果您需要有关BUILD.gn
语法的更多信息,还可以提供出色的介绍性介绍 。
We’ll be looking at how the d8
executable is constructed. This following is the code starting at line 4744 of my copy of BUILD.gn
(it’s a long file!). I’ve added the “section” comments to make the code easier to refer to.
我们将研究d8
可执行文件的构造方式。 以下是从BUILD.gn
副本的第4744行开始的代码(这是一个长文件!)。 我添加了“ section”注释,以使代码更易于引用。
# Section 1 - The v8_executable Template
v8_executable("d8") { # Section 2 - Defining the Sources
sources = [
"src/d8/async-hooks-wrapper.cc",
"src/d8/async-hooks-wrapper.h",
"src/d8/d8-console.cc",
"src/d8/d8-console.h",
"src/d8/d8-js.cc",
"src/d8/d8-platforms.cc",
"src/d8/d8-platforms.h",
"src/d8/d8.cc",
"src/d8/d8.h",
] # Section 3 - Optional Sources
if (v8_fuzzilli) {
sources += [
"src/d8/cov.cc",
"src/d8/cov.h",
]
} # Section 4 - Compilation Configuration
configs = [
":internal_config_base",
":v8_tracing_config",
] # Section 5 - Additional Dependencies
deps = [
":v8",
":v8_libbase",
":v8_libplatform",
":v8_tracing",
"//build/win:default_exe_manifest",
] ...
}
Let’s learn some of the main concepts of GN by walking through this example.
通过遍历此示例,让我们学习GN的一些主要概念。
Section 1 — The v8_executable
Template:
第1节 v8_executable
模板:
Out of the box, GN provides the executable
command for describing how to construct an executable program. For V8, we actually use the v8_executable
“template” (a GN concept) that wraps the basic executable
command, providing some additional functionality for compiling V8 executables. This template is defined by including import("gni/v8.gni")
at the top of the BUILD.gn
file. The v8.gni
file itself contains this snippet of code:
GN提供了开箱即用的executable
命令,用于描述如何构造可执行程序。 对于V8,我们实际上使用了v8_executable
“模板”(GN概念),该模板包装了基本的executable
命令,并提供了一些用于编译V8可执行文件的附加功能。 通过在BUILD.gn
文件顶部包含import("gni/v8.gni")
定义此模板。 v8.gni
文件本身包含以下代码片段:
...template("v8_executable") {
executable(target_name) {
...
}
...
}...
This file also contains similar templates for v8_static_library
, v8_shared_library
, and v8_source_set
that build upon the corresponding GN standard commands. In addition, the main BUILD.gn
file also contains some template definitions, making the build description more concise by abstracting away the complexity.
该文件还包含基于相应GN标准命令的v8_static_library
, v8_shared_library
和v8_source_set
类似模板。 此外,主BUILD.gn
文件还包含一些模板定义,从而通过简化复杂性来使构建描述更加简洁。
Section 2 — Defining the Sources:
第2节-定义来源:
To specify the C++ source files to be included in the d8
executable, we define a variable that contains a list of file paths. The GN tool supports a simple programming language, including the concept of variables and values, as well as lists of values.
为了指定d8
可执行文件中包含的C ++源文件,我们定义了一个包含文件路径列表的变量。 GN工具支持一种简单的编程语言,包括变量和值的概念以及值列表。
sources = [
"src/d8/async-hooks-wrapper.cc",
"src/d8/async-hooks-wrapper.h",
...
]
Note that unlike many build tools, we’re only required to list the file paths. We don’t need to construct file name pattern matching, or specify dependencies between files. The mechanism for doing that is hidden from you in the auto-generated .ninja
files.
请注意,与许多构建工具不同,我们只需要列出文件路径。 我们不需要构造文件名模式匹配,也不需要指定文件之间的依赖关系。 自动生成的.ninja
文件中隐藏的操作机制。
Section 3 — Optional Sources:
第3节-可选来源:
There are numerous build variants for V8, supporting a wide range of host platforms, target CPUs, optimization choices, JavaScript language-level selection, and additional feature libraries. To support all these variants, GN provides an if
statement for us to test variables and conditionally modify the list of sources (using sources +=
)
V8有许多构建变体,支持各种主机平台,目标CPU,优化选择,JavaScript语言级选择以及其他功能库。 为了支持所有这些变体,GN为我们提供了一个if
语句来测试变量并有条件地修改源列表(使用sources +=
)
if (v8_fuzzilli) {
sources += [
"src/d8/cov.cc",
"src/d8/cov.h",
]
}
In this particular example, we’re adding support for the Fuzzilli fuzzing tool which requires additional code-coverage functionality.
在此特定示例中,我们添加了对Fuzzilli模糊测试工具的支持,该工具需要其他代码覆盖功能。
Section 4 — Compilation Configuration:
第4节-编译配置:
To specify additional compilation flags for the d8
target, we make reference to a couple of “configs”:
为了为d8
目标指定其他编译标志,我们参考几个“ configs”:
configs = [
":internal_config_base",
":v8_tracing_config",
]
Here’s the definition of internal_config_base
that appears earlier in the BUILD.gn
file.
这是在BUILD.gn
文件中较早出现的internal_config_base
的定义。
config("internal_config_base") {
visibility = [ ":*" ] configs = [ ":v8_tracing_config" ] include_dirs = [
".",
"include",
"$target_gen_dir",
]
}
A “config” is a way to package together include paths, C++ symbol definitions, compiler flags, and additional libraries. These configs can obviously become quite complex, especially with support for multiple host platforms. But luckily, build targets simply need to reference the config by name, rather than worrying about all of those details.
“配置”是一种将包含路径,C ++符号定义,编译器标志和其他库打包在一起的方法。 这些配置显然会变得非常复杂,尤其是在支持多个主机平台的情况下。 但是幸运的是,构建目标只需要按名称引用配置,而不用担心所有这些细节。
Section 5 — Additional Dependencies:
第5节-其他依赖关系:
Finally, to specify additional source files, or libraries to be linked into the d8
executable, we define the deps
variable. Each entry in the list specifies a V8 build target, which itself provides a static/shared library, or a set of source files to include.
最后,要指定其他源文件或要链接到d8
可执行文件的库,我们定义deps
变量。 列表中的每个条目都指定一个V8构建目标,该目标本身提供了一个静态/共享库或要包含的一组源文件。
deps = [
":v8",
":v8_libbase",
":v8_libplatform",
":v8_tracing",
"//build/win:default_exe_manifest",
]
That’s it! A relatively simple way of specifying how to construct the d8
executable, without burdening the developer with the complexities of compilation flags, dependencies, and file pattern matching. There are plenty of other GN commands/directives that we haven’t discussed, but the GN documentation shows them all.
而已! 一种指定如何构造d8
可执行文件的相对简单的方法,而不会给开发人员增加编译标志,依赖项和文件模式匹配的复杂性。 我们还没有讨论很多其他的GN命令/指令,但是GN文档显示了所有这些信息。
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/f60cdfb39309f07cc184a3c7f4643725.png)
忍者制作工具 (The Ninja Build Tool)
The last step in the V8 build process (with the exception of running tests) is to invoke the Ninja Build Tool to generate the object files, libraries, and executables. Given that users aren’t expected to look at the auto-generated .ninja
files, there’s no need to look at further examples. However, it’s interesting to learn more about invoking Ninja, and the various command-line options available.
V8构建过程的最后一步(运行测试除外)是调用Ninja Build Tool来生成目标文件,库和可执行文件。 鉴于不希望用户查看自动生成的.ninja
文件,因此无需查看其他示例。 但是,了解有关调用忍者的更多信息以及可用的各种命令行选项很有趣。
速度就是一切 (Speed is Everything)
One of the interesting selling points of Ninja is its raw speed. Given my extensive history of using build tools like Make, I was very curious about what makes Ninja so responsive. When dealing with hundreds (or thousands) of source files, a lot of build tools will “pause” for 20–30 seconds as they determine which files are out of date. With Ninja, incremental builds seem to start instantly.
忍者有趣的卖点之一是它的原始速度。 考虑到我使用Make等构建工具的悠久历史,我对Ninja为何具有如此高的响应能力感到非常好奇。 当处理数百(或数千)个源文件时,许多构建工具将“暂停” 20-30秒,因为它们确定哪些文件已过期。 使用Ninja,增量构建似乎可以立即开始。
Here are some interesting facts about Ninja:
以下是有关忍者的一些有趣的事实:
First, the build description files (with
.ninja
suffix) are very simplistic. There is no complicated language to be parsed, and no advanced features requiring time to execute. For this reason, the documentation describes the syntax as “machine code”. The.ninja
files are also very compact, often with minimal white space. Keeping them small and simple makes them fast to read into memory, and to parse.首先,构建描述文件(带
.ninja
后缀)非常简单。 无需解析复杂的语言,也不需要高级功能才能执行。 因此,文档将语法描述为“机器代码”。.ninja
文件也非常紧凑,通常只有很少的空白。 保持它们的小巧简单可以使它们快速地读入内存并进行解析。Second, implicit dependencies are stored in a single cache file, the
.ninja_deps
file (a 2MB binary file on my computer). In a Make-based environment, it’s common to have a unique.d
file corresponding to each.cc
file to store the list of C++ header files depended-on by the main.cc
file. As a result, the build tool parses a very large number of files each time an incremental build is invoked. However, for Ninja, reading the one-and-only.ninja_deps
file is extremely fast!其次,隐式依赖项存储在单个缓存文件中,即
.ninja_deps
文件(我计算机上的2MB二进制文件)。 在基于Make的环境中,通常有一个与每个.cc
文件相对应的唯一.d
文件,以存储主要.cc
文件所依赖的C ++头文件的列表。 结果,每次调用增量构建时,构建工具都会解析大量文件。 但是,对于Ninja来说,读取一个唯一的.ninja_deps
文件非常快!
In the case of V8, the ninja
tool starts reading build.ninja
, which then imports the toolchain.ninja
file. It’s this second file that recursively imports all the other .ninja
files in the out/x64.release
directory (shown earlier). Despite having roughly 100 .ninja
files, reading and processing them is very fast.
对于V8, ninja
工具开始读取build.ninja
,然后将其导入toolchain.ninja
文件。 这是第二个文件,它递归导入out/x64.release
目录(如前所示)中的所有其他.ninja
文件。 尽管有大约100个.ninja
文件,但读取和处理它们非常快。
忍者命令行选项 (Ninja Command-Line Options)
To finish off, let’s show some of the commonly-used Ninja commands. To list all the available built targets, use the targets
command:
最后,让我们展示一些常用的忍者命令。 要列出所有可用的已构建目标,请使用targets
命令:
$ ninja -t targetsbuild.ninja: gn
obj/test/common_test_headers.inputdeps.stamp: stamp
obj/test/unittests/unittests.inputdeps.stamp: stamp
cppgc: phony
cppgc_base: phony
cppgc_for_testing: phony
fuzzer_support: phony
generate_bytecode_builtins_list: phony
...
:d8: phony
:fuzzer_support: phony
:gen-regexp-special-case: phony
:generate_bytecode_builtins_list: phony
:gn_all: phony
Naturally, these build targets are similar to what was shown in the BUILD.gn
file, and reported by the gn ls
command.
自然,这些构建目标类似于BUILD.gn
文件中显示的BUILD.gn
,并由gn ls
命令报告。
To compile a specific target, just mention it on the command line, optionally with the -v
flag if you want to see the underlying C++ compiler invocations.
要编译特定目标,只需在命令行中提及它即可,如果要查看基础C ++编译器调用,可以选择使用-v
标志。
$ ninja -v d8[1/1506] ../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF ...
[2/1506] ../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF ......[1506/1506] ...
To show all of the compilation commands required to build a target, without actually invoking the compiler, use the commands
option:
要显示构建目标所需的所有编译命令,而无需实际调用编译器,请使用commands
选项:
$ ninja -t commands d8...
Finally, to show where a particular file is used (that is, which libraries or executables depend on it), use the query
command:
最后,要显示使用特定文件的位置(即哪个库或可执行文件依赖于该文件),请使用query
命令:
$ ninja -t query ./obj/v8_libbase/mutex.oobj/v8_libbase/mutex.o:
input: cxx
../../src/base/platform/mutex.cc
outputs:
obj/libv8_libbase.a
obj/libwee8.a
These are the basics, but for more advanced options, see the Ninja documentation for further detail.
这些是基础知识,但是有关更多高级选项,请参阅Ninja文档以获取更多详细信息。
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/f60cdfb39309f07cc184a3c7f4643725.png)
摘要 (Summary)
The V8 JavaScript engine has an excellent build system, comprised of a top-level convenience script (gm.py
), which invokes the GN meta-build tool to generate lower-level build description files to be executed by the Ninja build tool. This combination of tools allows developers to work with the human-readable BUILD.gn
file format, while allowing for a fast execution of the build steps, especially for incremental build.
V8 JavaScript引擎具有出色的构建系统,该构建系统由顶级便捷脚本( gm.py
)组成,该脚本调用GN元构建工具来生成要由Ninja构建工具执行的较低级构建描述文件。 这种工具组合使开发人员可以使用人类可读的BUILD.gn
文件格式,同时允许快速执行构建步骤,尤其是增量构建。
翻译自: https://medium.com/compilers/v8-javascript-engine-compiling-with-gn-and-ninja-8673e7c5e14a
ninja gn