|
淺析鴻蒙中的 Gn 與 Ninja(一),
本帖最后由 delphi_tang 于 2021-1-27 17:10 編輯
鴻蒙系統(tǒng)的編譯構(gòu)建是基于 Gn 和 Ninja 完成的,那么 Gn 和 Ninjia 有什么關(guān)系呢?具體又是如何工作的呢?想必大多數(shù)熱衷于應(yīng)用開(kāi)發(fā)的同學(xué)都還沒(méi)有深究過(guò),那么今天就借此機(jī)會(huì)帶著大家扒一扒 Gn 和 Ninja。
我們先來(lái)說(shuō)說(shuō) Ninja 吧!
Ninja 是借由 Google Chrome 項(xiàng)目而誕生的一個(gè)構(gòu)建工具,它的誕生目標(biāo)是為了速度。換句話說(shuō),在 Google Chrome 項(xiàng)目的開(kāi)發(fā)過(guò)程中,開(kāi)發(fā)者們認(rèn)為同類(lèi)型的其它構(gòu)建工具不給力,所以才會(huì)考慮重新開(kāi)發(fā)更高效的工具。要說(shuō)同類(lèi)型,那么不得不提構(gòu)建界的老大哥 make !make 即 GNU Make,一個(gè)用于決定如何使用命令完成最終目標(biāo)構(gòu)建的程序。
在這里強(qiáng)調(diào) make 的 3 個(gè)特性:
- make 只是一個(gè)通用程序,它不知道如何具體的完成目標(biāo)的構(gòu)建工作
- make 需要 makefile 中的描述來(lái)決定目標(biāo)構(gòu)建的具體方案
- make 需要借助其它工具(如:gcc)才能執(zhí)行方案,最終完成工作
這是不是跑題了!不是說(shuō)好的討論 Ninja 嗎?怎么扯到 make 上去了?!
因?yàn)?Ninja 可以看作是一個(gè)更好的 make !而大多數(shù)同學(xué)都熟悉 make ,所以通過(guò)對(duì)比 make 學(xué)習(xí) Ninja 是一個(gè)非常好的選擇!上述關(guān)于 make 的 3 個(gè)特性對(duì)于 Ninjia 同樣適用(理論上,make 有的 Ninjia 都有,并且更好!)。那么,是不是得先學(xué)習(xí) make 再學(xué)習(xí) Ninja 呢?我覺(jué)得倒也不是!畢竟我們最終還是在鴻蒙上做應(yīng)用開(kāi)發(fā),編譯構(gòu)建系統(tǒng)只需要大體了解即可。
接下來(lái)通過(guò)一個(gè)簡(jiǎn)單的例子向大家展示 Ninja 的用法!
test.c 是一個(gè)簡(jiǎn)單的 Hello World 程序,用于打印一個(gè)字符串和頭文件 test.h 中常量 CONST 的值。
根據(jù) C 程序的編譯方式可知:
- 在預(yù)處理階段 test.h 中的代碼直接嵌入test.c 中(頭文件 .h 最終成為源文件 .c 的一部分)
- test.c 編譯后得到目標(biāo)文件 test.o
- test.o 鏈接后得到最終的可執(zhí)行程序 test.out
各個(gè)文件在編譯過(guò)程中有明顯的上下游關(guān)系,即:上游文件影響或者產(chǎn)生下游文件。
上圖即描述了編譯過(guò)程,同時(shí)也反映了這樣一個(gè)事實(shí):任何一個(gè)文件被改動(dòng)時(shí)只可能影響下游文件,而不會(huì)影響上游文件。如:test.c 被修改了,那么可能導(dǎo)致編譯得到 test.o 發(fā)生改變,進(jìn)而導(dǎo)致最終的可執(zhí)行程序 test.out 改變。因此,當(dāng) test.c 被修改時(shí),那么應(yīng)該重新觸發(fā)編譯和鏈接這兩個(gè)動(dòng)作。
看到這里,有同學(xué)可能存在這樣的疑問(wèn):怎么知道文件已經(jīng)被修改了并觸發(fā)相應(yīng)動(dòng)作呢?
其實(shí)很簡(jiǎn)單,可以根據(jù)文件修改時(shí)間判斷呀!目前幾乎主流的文件系統(tǒng)都會(huì)記錄文件被修改的時(shí)間,所以結(jié)合文件的上下游關(guān)系可知:上游文件被修改的時(shí)間應(yīng)該總是
小于等于 下游文件被修改的時(shí)間。這樣,只需要遍歷一次上面的構(gòu)建圖就可以知道執(zhí)行哪些動(dòng)作產(chǎn)生最終可執(zhí)行程序了。
接下來(lái)思考這樣一個(gè)問(wèn)題:如何向構(gòu)建工具 Ninja 描述構(gòu)建圖?
Ninja 的本質(zhì)是一種通用程序。既然是程序,那么擅長(zhǎng)的必然是處理結(jié)構(gòu)化文本!因此,可以用結(jié)構(gòu)化文本(Ninja腳本)來(lái)描述構(gòu)建圖。
下面直接上代碼!
解讀:
1. Ninja 腳本中的
build 語(yǔ)句描述構(gòu)建圖中的一個(gè)文件上下游關(guān)系。如:
build test.o
cc test.c 指明 test.o 由 test.c 通過(guò)規(guī)則
cc 而構(gòu)建,test.c 在構(gòu)建圖中位于 test.o 的上游,從 test.c 到 test.o 需要執(zhí)行的動(dòng)作通過(guò)規(guī)則
cc 定義。Ninja 通過(guò)判斷上下游文件的修改時(shí)間決定是否執(zhí)行規(guī)則中定義的動(dòng)作。多個(gè)
build 語(yǔ)句共同描述一個(gè)編譯構(gòu)建圖。
2. Ninja 腳本中通過(guò)
rule 定義規(guī)則描述構(gòu)建圖中需要執(zhí)行的動(dòng)作。如:規(guī)則
cc 所定義的具體動(dòng)作是 gcc -c $in -o $out ,其中 $in 指代上游文件, $out 指代下游文件。對(duì)于
build test.o
cc test.c 而言,最終執(zhí)行的動(dòng)作為:gcc -c test.c -o test.o 。
3. 由 C 語(yǔ)言及其編譯方式可知:當(dāng)源文件包含的頭文件改動(dòng)時(shí),源文件需要重新編譯。因此,在構(gòu)建圖中頭文件順理成章的成為了源文件的上游文件,需要考慮的僅僅是如何定義 rule 最終觸發(fā)編譯動(dòng)作。這里使用的技巧是通過(guò)命令 touh 更新源文件的修改時(shí)間,于是可定義 rule
dp 的執(zhí)行動(dòng)作為 touch $out。這樣
build test.c :
dp test.h 的意思就很清楚了:當(dāng) test.h 被修改時(shí),執(zhí)行 touch test.c 更新修改時(shí)間,進(jìn)而觸發(fā)重新編譯。
4.
default test.out 指明默認(rèn)構(gòu)建的目標(biāo)是 test.out,即: ninja 執(zhí)行當(dāng)前腳本時(shí)默認(rèn)編譯構(gòu)建的是 test.out。
理解了 Ninja 腳本的基本構(gòu)成后就可以通過(guò)實(shí)驗(yàn)進(jìn)一步體會(huì)了!
1. 將上面的腳本另存為文件,并重命名為 build.ninja,且與 test.c 和 test.h 位于同一目錄下
2. 打開(kāi)命令行定位到源碼目錄,執(zhí)行 ninja > log.txt
通過(guò)編譯輸出(log.txt)以及 test.out 的運(yùn)行結(jié)果可知目標(biāo)構(gòu)建成功。
后記:
這只是一個(gè) Ninja 的入門(mén)級(jí)介紹,更多的細(xì)節(jié)大家可以參考附件中的手冊(cè)。同時(shí),文中的示例代碼也可以在附件中下載。大家可以自己動(dòng)手修改源碼(比如:修改 test.h 中 CONST 的值)然后自行編譯體會(huì) Ninja 的用法。
Enjoy it! |
|