V8 is a familiar concept, so have you compiled V8 source code? Have you tried to understand some of the concepts behind V8 since it was compiled? If not, don’t panic. I’ll explain everything below. Before we build V8, there is one thing we need to know – the build system

1. Build the system

1.1. What is the build system?

Writing used to the front of the children’s shoes may not understand this thing is to do what? But you do it all the time. It’s just a different concept. The front end is often called a packaged build, similar to what tools such as WebPack and Parcel do. In fact, the final goal is to get some target documents. A brief mention of the history of build systems in software engineering can be made here.

The need to build systems arises as the software scale increases. If you just do a simple demo, usually the code is relatively small, the source code prepared only a few files. For example, if you write a piece of code to put in the helloworld.cpp file, to compile the code, simply execute the following command:

g++ helloworld.c -o helloworld
Copy the code

As software grows in size, with dozens of source code files, and modules that have to be compiled into static libraries and dynamic libraries, and then linked into executable code, the command-line approach becomes overstretched and requires a build system. A common build system is GNU Make. It is important to note that the system is not built to replace a toolchain like GCC, but to define compilation rules that will eventually call the toolchain to compile code.

Writing GNU Makefiles can be tedious, tedious, and error-prone as software scales up, especially with the need for multi-platform support. This is where tools like Cmake, AutoMake, etc., come in to generate Makefiles, called meta Build Systems. Before the concept of a software repository became common on Linux, the usual steps for installing software were as follows:

./configure
make
make install
Copy the code

The first step is to call some automation tools and generate a GNU Makefile based on the environment (there are many versions of the system and the software installation varies). The second step is to compile all the files using either GCC or g++, and the last step is to link all the files together into executable commands and install them in a specific directory on your system.

After general two steps are more solidified, can improve work efficiency is in the first step. The V8 team developed a build system called GYP(Generate Your Projects) for their own purposes, and as you’ll see later, Node-gyp is actually a JS version of this. However, GYP was scrapped by the V8 team and GN(Generate Ninja) was used to build the system. The difference between the two is not the point of this article, but for those interested, check out GN build System in Chromium.

It’s interesting to note that nodeJS still uses GYP, even though V8 scrapped it completely. This was mentioned in the deno project: Design Mistakes in Node.

1.1.1 introduction to GN Construction System

GN(Generate Ninja) is a new tool used by the chromium project to replace GYP. Since GN is written in C++, it is much faster than GYP written in python, and the syntax of GN’s new DSL is also considered to be easier to write and maintain.

In the root directory of the V8 project there is a.gn file that looks like this (all comments removed) :

import("//build/dotfile_settings.gni")
buildconfig = "//build/config/BUILDCONFIG.gn"
check_targets = []
exec_script_whitelist = build_dotfile_settings.exec_script_whitelist + []
Copy the code

Let’s focus on the buildConfig configuration. . Directory will be designed.the gn is designed.the gn tool as the root directory of the project, the basic is designed.the gn content buildconfig is used to specify the build the location of the config, among them / / build / / config/buildconfig designed.the gn is relative to the project root directory path configuration file.

However, you will notice that there is currently no directory called build in the V8 source directory. How to generate this directory? We’ll cover this later in compiling v8 code.

Assuming you now have the build directory, we’ll find the buildConfig. gn file, which sets the build chain for each system and platform:

. .if(custom_toolchain ! ="") {
  set_default_toolchain(custom_toolchain)
} else if(_default_toolchain ! ="") { set_default_toolchain(_default_toolchain) } ... .Copy the code

For example, the resulting _default_toolchain value is: _default_toolchain = “/ / build/toolchain/Linux: clang_x86, so you are in the build/toolchain/Linux directory build designed.the gn can find such a configuration:

clang_toolchain("clang_x86") {
  # Output linker map files for binary size analysis.
  enable_linker_map = true

  toolchain_args = {
    current_cpu = "x86"
    current_os = "linux"}}Copy the code

GN does not have built-in toolchain rules. Toolchain tools such as cc, CXX and link must be specified in build/toolchain/ gcc_toolchain-gni files. In the file we can see some actions defined by the GN:

tool("cc") {
  depfile = "{{output}}.d"
  precompiled_header_type = "gcc"
  command = "$cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}${extra_cppflags}${extra_cflags} -c {{source}} -o {{output}}"
  depsformat = "gcc"
  description = "CC {{output}}"
  outputs = [
    "$object_subdir/{{source_name_part}}.o"]},Copy the code

Finally, there is a build. gn file in the project root directory that specifies the instructions to generate the executable, such as:

v8_executable("v8_hello_world") {
  sources = [
    "samples/hello-world.cc",
  ]

  configs = [
    # Note: don't use :internal_config here because this target will get
    # the :external_config applied to it by virtue of depending on :v8, and
    # you can't have both applied to the same target.
    ":internal_config_base",
  ]

  deps = [
    ":v8".":v8_libbase".":v8_libplatform"."//build/win:default_exe_manifest"]},Copy the code

Thus a complete GN build system is completed.

1.1.2 Ninja builds the system

Why would you need Ninja if you had the GN? As GN stands for Generator Ninja, this is not our final GNU Makefile. Ninja is the ultimate tool to generate the Makefile. Ninja is a new compilation tool that is small, efficient and, according to Google, several times faster.

We haven’t generated any Ninja files yet, so we need to use GN command to generate them:

gn args out/foo

Now you can see a bunch of Ninja files under out/foo:

Ninja uses the build. Ninja file to define the build rules. Unlike metaprogramming in makefiles, Build. Ninja is almost completely static, with dynamic generation relying on other tools such as GN or CMake.

build.ninja

Build. niinja is the ninja makefile, a simple build.ninja file split into rule and Dependency.

Phony: you can create aliases for other targets.

Default: If you do not specify target on the command line, you can use default to specify the default target.

Pools: In order to support concurrent jobs, Ninja also supports the pool mechanic as well as the -j parallel mode.

Make vs Ninja Performance Comparison

Compile and test the V8 code

Let’s start compiling the V8 code. The official website has provided a complete set of documents. Here is just a brief introduction and some basic knowledge that is not provided on the official website.

2.1 download the V8 code

Do not download the code directly from the v8 repository using the git clone command. If you download the code, you will not be able to use the depot_tools tool

The steps are summarized as follows:

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
Copy the code

2.2 compile the V8 code

The website for compiling v8 code is also very detailed: Portal, just to summarize, there are two ways to compile

2.2.1 super convenient way

With gm, the all-in-one Python script can be done with a few commands:

alias gm=/path/to/v8/tools/dev/gm.py
gm x64.release
gm x64.release.check
Copy the code

2.2.2 Manual compilation

Following the process we described earlier, we need to use GN to generate the Ninja file, regenerate it into a makefile, and finally compile it, so:

Gn args out/foo or gn Gen out/foo –args=’is_debug=false target_CPU =”x64″ v8_target_CPU =”arm64″ Use_goma =true’ to generate the Ninja file.

This line of command is not explained in detail on the official website, but I will explain it here:

Gn args out/foo => Specify the output directory as an argument. Gn gen out/foo => Specify the output directory as an argument: --args='is_debug=false target_cpu="x64" v8_target_cpu="arm64" use_goma=true'Gn args out/foo --list => see what parameters were configured to build the output directoryCopy the code

If this is too much trouble, v8 provides another script to integrate these steps: v8gen, with the following command:

alias v8gen=/path/to/v8/tools/dev/v8gen.py
v8gen -b 'V8 Linux64 - debug builder' -m client.v8 foo
Copy the code

V8gen works with the mb_config.pyl file. To generate the build file, use the master configuration (-m) and Builder configuration (-b), which we found in mb_config.pyl:

The last parameter foo specifies the generated secondary directory. The default primary directory is out.gn, as follows:

You can also use the default configuration and go straight to V8Gen Foo

Next use Ninja to compile:

ninja -C out/foo

If you want to specify build specify target:

ninja -C out/foo d8

Goma /gomacc: No such file or directory. Since we don’t have goma installed locally, we need to install goma to compile properly. What is Goma? Goma is a compilation acceleration tool that you can use on your website

Compile a single C++ file that references the v8 library

If you want to compile a single file using v8, such as the g++ command in hello. cc, there are some parameters that you must know about the g++ command.

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

The explanation of the G++ command is as follows:

-std= Determines the language standard to use, which configuration should be supported when compiling C and C++. The 'c++0x' in the above command means: the language standard uses a working draft of the forthcoming ISO c++0x standard. This option supports experimental features that may be included in c++ 0x. Working drafts are constantly changing, and any features enabled by this flag may be removed if a future version of GCC is not part of the c++ 0x standard. More standard refer to: [g] (https://linux.die.net/man/1/g++) - use the POSIX pthread threads library to add support for multithreaded. This option sets flags for the preprocessor and linker. It does not affect the thread-safety of the object code generated by the compiler or the libraries it provides. These are hP-UX-specific flags. -i dir Adds the directory dir to the list of directories to search for header files. Searches for directories specified by ** -i ** before the system standard includes directories. If directory *dir* is a standard system include directory, this option is ignored to ensure that the default search order for system directories and special treatment of system headers are not broken. If * dir *"="At the beginning,"="Will be replaced by the sysroot prefix. -o file Specifies the output file. This is the same as specifying file as the second non-option parameter to CPP. GCC has a different interpretation of the second non-option argument, so you must use -o to specify the output file -llibrary-lThe library link searches for a library named Library. (The second way of specifying library files is only for POSIX compliance and is not recommended.) Where you write this option in the command varies; The linker searches and processes libraries and object files in the specified order. Therefore, 'foo.o -lz bar.o' searches library Z after the file foo.o. But before bar.o. If bar.o is a function referenced in the z library, these functions cannot be loaded. The standard directory list that the linker searches for libraries is actually a file called 'liblibrary.a'. The linker then uses the file as if it were specified precisely by name. The directories searched include several standard system directories, as well as any directories you specify using -l. The files typically found in this way are library files -- archives whose members are the target files. The linker processes archive files by scanning members that define symbols that have been referenced so far but not yet defined. However, if the file found is a plain object file, it is linked in the usual way. -ldir Adds the 'dir' directory to the search directory list-lUse `Copy the code

So the above command must be clear

4. Brief introduction of basic concepts of V8 engine

[真 题] Many of the concepts are described in full detail in the advanced stages of V8 learning, but here they are simplified to make them more memorable.

4.1, isolate

This concept is not mentioned in the advanced progression of V8 learning, which represents a separate V8 virtual machine, with its own stack. That’s why it got the name ISOLATE, which means “isolate”. Initialize in V8 using the following syntax:

Isolate* isolate = Isolate::New(create_params);
Copy the code

4.2, handle…

Handle is a pointer to an object. In V8, all objects are referred to by Handle, which is used primarily for garbage collection in V8. In V8, the Handle is divided into two types: Persistent Handle and Local Handle. The Persistent handle is stored on the heap, while the Local handle is stored on the stack. For example, if I want to use a local handle that points to a string, you define it like this:

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

V8 also provides HandleScope for batch handling. You can declare this before handling:

HandleScope handle_scope(isolate);

4.3, the context

Context is an executor environment that allows separate JavaScript scripts to run in the same V8 instance without interference. To run a JavaScript script, you need to explicitly specify the context object. To create a context, do this:

// Create a Context Local<Context> Context = Context::New(ISOLATE); // Enter the Context to compile and run the script Context::Scope context_scope(Context);Copy the code

4.4 V8 data type

Since C++ native Data types are very different from JavaScript Data types, V8 provides the Data class. This class and its subclasses are used from JavaScript to C++, and from C++ to JavaScrpt, for example:

String::NewFromUtf8(info.GetIsolate(), "version").ToLocalChecked()
Copy the code

Here String is V8’s data type. Such as:

v8::Integer::New(info.GetIsolate(), 10);
Copy the code

4.5 object templates and function templates

These two template classes are used to define JavaScript objects and JavaScript functions. We’ll look at examples of template classes in a later section. ObjectTemplate is used to expose C++ objects to scripting environments, and FunctionTemplate is used to expose C++ functions to scripting environments for use by scripts.

The last

At this point, v8 should have a basic understanding of v8. There are many important concepts in v8, so if you want to continue, you can refer to another article about v8 in practice: how to properly use v8 embedded in our C++ applications

reference

  1. GN build system in Chromium
  2. GYP, GN and Ninja
  3. depot_tools_tutorial(7) Manual Page
  4. GN Reference