nodejs missing script: dev_nodejs深入學習系列之v8基礎篇

V8這個概念大家都不陌生了,那么你動手編譯過V8源碼嗎?編譯后有嘗試去了解V8背后的一些概念嗎?如果沒有,那么也不用心慌,下文將跟大家一一解釋這些東西。在編譯V8之前我們先要了解一個東西-構建系統

1、構建系統

1.1、構建系統是啥?

寫慣前端的童鞋可能不是很明白這個東西是干啥用的?但是其實平時你都會接觸到,只是概念不同而已。前端我們一般稱其為打包構建,類似工具諸如webpack、parcel做的事情。其實最后的目標都是想得到一些目標性的文件。這里可以簡單地提及一下軟件工程中的構建系統的歷史。

構建系統的需求是隨著軟件規模的增大而提出的。如果只是做簡單的demo,通常代碼量比較小,編寫的源代碼只有幾個文件。比如你編寫了一段代碼放入helloworld.cpp文件中,要編譯這段代碼,只需要執行以下命令:

g++ helloworld.c -o helloworld

當軟件規模逐漸增加,這時可能有幾十個源代碼文件,而且有了模塊劃分,有的要編譯成靜態庫,有的要編譯成動態庫,最后鏈接成可執行代碼,這時命令行方式就捉襟見肘,需要一個構建系統。常見的構建系統有GNU Make。需要注意的是,構建系統并不是取代gcc這樣的工具鏈,而是定義編譯規則,最終還是會調用工具鏈編譯代碼。

當軟件規模進一步擴大,特別是有多平臺支持需求的時候,編寫GNU Makefile將是一件繁瑣和乏味的事情,而且極容易出錯。這時就出現了生成Makefile的工具,比如CmakeAutoMake等等,這種構建系統稱作元構建系統(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。

acda9eaf9b2a8fb607424a59d7b159b8.png

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 = "x86"current_os = "linux"}
}

因為GN沒有內建的toolchain規則,toolchain里的各種tool例如 cc,cxx,link等必須自己指定,指定的文件是build/toolchain/gcc_toolchain.gni文件,在文件中我們可以看到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",]
}

最后項目根目錄下會有一個BUILD.gn的文件,指定生成可執行文件的指令,比如:

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",]
}

這樣一套完整的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文件:

527b623e944539007c70d35d96804117.png

Ninja使用build.ninja文件來定義構建規則,和Makefile里的元編程不同,build.ninja幾乎是完全靜態的,動態生成依賴其他工具,如gn或者CMake。

build.ninja

build.niinja相當于ninja的makefile,一個簡單的build.ninja文件如下,分為rule和dependency兩部分。

b89a2602c8f108680bdc9a3d391880d4.png

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 --args='is_debug=false target_cpu="x64" v8_target_cpu="arm64" use_goma=true'來生成ninja文件。

這一行命令官網沒有詳細解釋,我在這里解釋一下:

gn args out/foo => 通過參數形式指定輸出目錄,這個命令會彈出文本讓你配置參數
gn gen out/foo => 指定GN構建輸出的目錄, 可以指定參數: --args='is_debug=false target_cpu="x64" v8_target_cpu="arm64" use_goma=true',這個命令不會彈出文本窗讓你配置
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找到對應的配置:

3f033908a4bc0179b133df481380cf33.png

最后一個參數foo是指定生成的二級目錄,默認一級目錄是out.gn,如下:

d4f6ceb624584211cf2ad7d38e8fad6d.png

你也可以使用默認配置,直接v8gen foo

接下去使用ninja來編譯:

ninja -C out/foo

如果想要指定生成指定目標則:

ninja -C out/foo d8

上述編譯正常會報錯:goma/gomacc: No such file or directory。因為我們本地沒有安裝goma,所以想要正常編譯下去,還需要安裝一下goma,goma是什么東西呢?從官網上看,它是一個輔助編譯加速的工具,詳細可以參考:goma

3、編譯單個引用到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 -std=c++0x

G++命令解釋如下:

-std=決定使用的語言標準,當編譯C和C++的時候該選擇支持配置。上述命令中的`c++0x`表示:語言標準使用即將發布的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、handle

handle是指向對象的指針,在V8中,所有的對象都通過handle來引用,handle主要用于V8的垃圾回收機制。在 V8 中,handle 分為兩種:持久化 (Persistent)handle 和本地 (Local)handle,持久化 handle 存放在堆上,而本地 handle 存放在棧上。比如我要使用本地句柄,句柄指向的內容是一個string,那么你要這么定義:

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

鑒于一個個釋放Handle比較麻煩,v8又提供了HandleScope來批量處理,你可以在handle之前聲明好:

HandleScope handle_scope(isolate);

4.3、context

context 是一個執行器環境,使用 context 可以將相互分離的 JavaScript 腳本在同一個 V8 實例中運行,而互不干涉。在運行 JavaScript 腳本是,需要顯式的指定 context 對象。創建上下文,需要這樣:

// 創建一個上下文
Local<Context> 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++應用中

參考

  1. chromium中的GN構建系統
  2. GYP,GN和Ninja
  3. depot_tools_tutorial(7) Manual Page
  4. GN Reference

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/533857.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/533857.shtml
英文地址,請注明出處:http://en.pswp.cn/news/533857.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

cmos存儲器中存放了_天津大學姚建銓院士,張雅婷副教授JMCC:具有寬光譜調控特性的阻變存儲器...

【引言】存儲器是計算機中數據存放的主要介質。隨著5G時代到來&#xff0c;帶動人工智能、物聯網、智慧城市等應用市場發展并向存儲器提出多樣化需求&#xff0c;加上傳統存儲器市場價格變化等因素&#xff0c;新型存儲器將在市場發揮越來越重要的作用。因此具有存儲密度更高&a…

matlab轉差頻率控制,轉差頻率控制的異步電機調速系統的研究

1 引言交流變頻調速的方法是異步電機最有發展前途的調速方法。隨著電力電子技術、計算機技術和自動控制技術的不斷發展&#xff0c;交流電機變頻調速已經逐步取代直流電機調速&#xff0c;并經歷了采用電壓頻率協調控制、轉差頻率控制、矢量控制以及直接轉矩控制的發展過程。其…

python中標識符的命名規則_Python——標識符的命名規則

01 Python語言的特點 python的語言特點有很多&#xff0c;我們這里只講一點&#xff0c;python是一門面向對象的語言&#xff0c;即一切皆對象&#xff08;Linux中有一句是&#xff1a;一切皆文件&#xff09;&#xff0c;括號內的只是打個比方&#xff0c;不懂也沒事&#xff…

python內置對象是什么_Python內置對象類型之數字類型

Python中有6種內置對象類型整數、浮點數–Number字符串–String列表–List元組–Tuple字典–Dictionary集合–Set不可變類型&#xff1a;Number、String、Tuple可變類型&#xff1a;List、Dictionary、Set知識點&#xff1a;變量和對象的關系–引用變量的使用數字類型的常見使用…

oracle的exp和imp,oracle exp和imp

--1.exp和imp的輸入都是名字和值對&#xff1a;如&#xff1a;exp parameter_namevalue 或exp parameter_name(value1,value2,value3..)--2.exp和imp都支持helpy選項。命令格式為:exp/imp helpy--3.exp中的參數:參數名稱 默認值 含義 建議compress Y 不壓縮導出數據的內容 comp…

python案例實操_用案例實操學習Python ,培養編程邏輯思維

案例一&#xff1a; A、B、C、D、E 五人在某天夜里合伙去捕魚&#xff0c;到第二天凌晨時都疲憊不堪&#xff0c;于是各自找地方睡覺。 日上三桿 A 第一個醒來&#xff0c;他將魚分為五份&#xff0c;把多余的一條魚扔掉&#xff0c;拿走自己的一份。 B 第二個醒來&#xff0c;…

oracle錯誤1327,Oracle中的PGA監控報警分析(r11筆記第97天)

最近接到一個數據庫報警&#xff0c;讓我頗有些意外&#xff0c;這是一個PGA相關的報警。聽起來感覺是應用端的資源調用出了問題。報警內容大體如下&#xff1a;報警內容: PGA Alarm on alltest------------------------------------報警級別: PROBLEM------------------------…

python控制臺清屏_Python Shell 怎樣清屏?

啟動Python有兩種方式&#xff0c;分別為“Windows命令行窗口”和“IDLE” “命令行窗口”下可以通過如下兩種方法&#xff1a; 1. import subprocess subprocess.call("clear") # linux/mac subprocess.call("cls", shellTrue) # windows 執行完次命令后&…

python卸載opencv包_Ubuntu16.04卸載opencv2.4.9并安裝opencv3.2.0+contrib

本文為作者原創&#xff0c;轉載請注明出處(http://www.cnblogs.com/mar-q/)by 負赑屃需要用到opencv中的surf和sift算法&#xff0c;機器上只有opencv3.2&#xff0c;沒有擴展包&#xff0c;于是就去GitHub和官網看了下&#xff0c;沒想到8月剛出了3.3&#xff0c;一個激動就想…

php函數內的循環,PHP 循環列出目錄內容的函數代碼

PHP 循環列出目錄內容的函數代碼復制代碼 代碼如下:function list_files($dir){if(is_dir($dir)){if($handle opendir($dir)){while(($file readdir($handle)) ! false){if($file ! "." && $file ! ".." && $file ! "Thumbs.db&quo…

python火柴人打架代碼_python火柴人

廣告關閉 騰訊云11.11云上盛惠 &#xff0c;精選熱門產品助力上云&#xff0c;云服務器首年88元起&#xff0c;買的越多返的越多&#xff0c;最高返5000元&#xff01; 代碼實現了一個火柴人&#xff0c;他開心時可以跳躍、可以舞蹈&#xff0c;不開心時可以躺地上... ?代碼有…

spring boot admin 2.2 獲取日志失敗_SB實戰20-Spring Boot的日志和報告

上篇我們學習了《SB實戰19-Spring Boot的外部配置》&#xff0c;本篇我們學習Spring Boot的日志和報告。4 日志和報告4.1 日志日志是對應用運行時進行調試和分析的重要工具。Spring Boot使用SLF4J作為日志的API&#xff0c;Logback、Log4j2、Java Util Logging都可以作為日志提…

oracle樹狀排序,Oracle樹狀結構查詢

oracle用表的形式組織數據&#xff0c;某些數據還呈現樹狀結構&#xff0c;提供了對這些數據的組織、查詢等功能。在掃描樹結構表時&#xff0c;要依次訪問樹中的每一個節點&#xff0c;并且每個節點只能訪問一次&#xff0c;其步驟如下&#xff1a;1&#xff1a;從根節點開始2…

python numpy讀取數據_大神教你python 讀取文件并把矩陣轉成numpy的兩種方法

導讀 今天小編就為大家分享一篇python 讀取文件并把矩陣轉成numpy的兩種方法&#xff0c;具有很好的參考價值&#xff0c;希望對大家有所幫助。一起跟隨小編過來看看吧 在當前目錄下&#xff1a; 方法1&#xff1a; file open(‘filename) a file.read() b a.split(‘\n)#使用…

datagrid wpf 獲取選中_c# WPF DataGrid 獲取選中單元格信息

private void Button_Click(objectsender, RoutedEventArgs e){DataGridCell cell dg.GetCell(1, 2);TextBlock tb cell.Content asTextBlock;Console.WriteLine(tb.Text);}public static classDataGridExtension{/// ///獲取DataGrid控件單元格/// /// DataGrid控件/// 單元格…

redis哨兵高可用-源碼篇

前段時間寫過兩篇redis哨兵的文章,一篇是redis哨兵模式的搭建。另外一篇是redis哨兵主從切換的原理,。 當時寫的原理篇,是手動模擬主節點故障,然后查看主從切換的日志推算哨兵主從切換的流程。但是感覺這樣搞出來的流程太粗&#xff0c;忽略了很多細節&#xff0c;真正要搞明白…

python獲取網頁數據對電腦性能_【Python】網頁數據爬取實戰

由于網頁結構跟之前有變化&#xff0c;還不是很熟悉。代碼待完善&#xff0c;問題記錄&#xff1a;騰訊新聞二級網頁內容爬取有問題。鏈家網站頭文件沒有用到。爬取一條騰訊視頻的header內容&#xff0c;存入txt。要求&#xff1a;包含網頁鏈接包含title包含所有headers信息imp…

python集合去重_python集合去重

[python中對list去重的多種方法 怎么快速的對列表進行去重呢&#xff0c;去重之后原來的順序會不會改變呢&#xff1f; 1.以下的幾種情況結果是一樣的&#xff0c;去重之后順序會改變: i [1,2,3,3,4,2,3,4,5,6,1] news_i [] for id in i: if id not in news_i: news_i.append(…

linux進程pid分配規則,Linux進程pid分配法【轉】

一. 概述Android系統創建進程&#xff0c;最終的實現還是調用linux fork方法&#xff0c;對于linux系統每個進程都有唯一的 進程ID(值大于0)&#xff0c;也有pid上限&#xff0c;默認為32768。 pid可重復利用&#xff0c;當進程被殺后會回收該pid&#xff0c;以供后續的進程pid…

sqlserver date類型和字符串比較_基于SQL Server數據庫搭建主從復制實現讀寫分離實戰演練...

一、課程介紹讀寫分離(主從同步)從字面意思就可以理解&#xff0c;就是把對數據庫的讀操作和寫操作分離開。讀寫分離在網站發展初期可以一定程度上緩解讀寫并發時產生鎖的問題&#xff0c;將讀寫壓力分擔到多臺服務器上。讀寫分離的基本原理是讓主數據庫處理事務性增、改、刪操…