新聞中心
??想了解更多關(guān)于開源的內(nèi)容,請訪問:??

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比仁布網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式仁布網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋仁布地區(qū)。費用合理售后完善,10余年實體公司更值得信賴。
?? 開源基礎(chǔ)軟件社區(qū)??
??https://ost.??
OpenHarmony使用gn+ninja來維護(hù)開源項目的構(gòu)建。之前沒有接觸過gn+ninja,是時候系統(tǒng)性的來學(xué)習(xí)下了。邊學(xué)邊記錄下學(xué)習(xí)過程,希望對同樣需要學(xué)習(xí)gn+ninja的朋友有所幫助。
這一篇,我們來學(xué)習(xí)GN的語法和操作行為等,建議也可以閱讀原版文檔??GN Language and Operation??。
GN提供了擴展的內(nèi)置幫助文檔系統(tǒng),提供每一個函數(shù)功能和內(nèi)置變量的詳細(xì)的參考引用??梢允褂胓n help來查看幫助,可以進(jìn)一步使用gn help
1、Design philosophy設(shè)計理念
- 編寫構(gòu)建文件不應(yīng)該是一項創(chuàng)造性的工作。理想情況下,兩個人應(yīng)該在相同的要求下生成相同的構(gòu)建文件。除非絕對需要,否則不應(yīng)該有靈活性。盡可能多的事情應(yīng)該是致命的錯誤。
- 構(gòu)建定義應(yīng)該讀起來更像代碼而不是規(guī)則。我不想編寫或調(diào)試Prolog。但是我們團(tuán)隊中的每個人都可以編寫和調(diào)試C++和Python。
- 構(gòu)建語言應(yīng)該對構(gòu)建應(yīng)該如何工作持固執(zhí)己見。表達(dá)武斷的東西不一定容易,甚至不可能。我們應(yīng)該改變源代碼和工具使構(gòu)建更簡單,而不是使一切都更復(fù)雜以符合外部要求(在合理范圍內(nèi))。
- 在有意義的時候,需要像Blaze一樣。
2、Language語言
GN使用機器簡單的,動態(tài)類型的語言。支持的類型有:
- Boolean (true, false). 布爾值。
- 64-bit signed integers. 64位有符號整數(shù)。
- Strings. 字符串。
- Lists (of any other types). 上述類型的列表。
- Scopes (sort of like a dictionary, only for built-in stuff). 作用域(類似字典)。
(1)Strings 字符串
字符串括在雙引號中,并使用反斜杠作為轉(zhuǎn)義字符。僅僅支持如下轉(zhuǎn)義序列是:
- \"(雙引號)
- \$(美元符號$)
- \\(反斜杠)
反斜杠的任何其他用法都被視為反斜杠。因此,例如,\b不需要轉(zhuǎn)義,大多數(shù) Windows 路徑如 "C:\foo\bar.h")不需要轉(zhuǎn)義。
通過符號$支持簡單變量替換,其中美元符號$后面的單詞被替換為變量的值。如果沒有非變量名稱字符來終止變量名稱,則可以選擇${}將名稱括起來。不支持更復(fù)雜的表達(dá)式,僅支持變量名稱替換。
a = "mypath"
b = "$a/foo.cc" # b -> "mypath/foo.cc"
c = "foo${a}bar.cc" # c -> "foomypathbar.cc"
(2)Lists列表
除了把非空列表賦值給空列表(a == [])之外,沒有辦法獲得列表的長度。如果你發(fā)現(xiàn)自己想做這種事情,意味著在構(gòu)建中做太多的工作。— 注:說的是,列表不提供獲取長度,也不應(yīng)該獲取長度。
列表追加
列表支持追加,如下所示。將一個列表追加到另一個列表,會把每一個列表項追加為第二個列表中的項,而不是將該列表追加為嵌套成員。
a = [ "first" ]
a += [ "second" ] # [ "first", "second" ]
a += [ "third", "fourth" ] # [ "first", "second", "third", "fourth" ]
b = a + [ "fifth" ] # [ "first", "second", "third", "fourth", "fifth" ]
列表刪除
還可以從列表中刪除項目,如下。列表中的減號運算符“-”搜索匹配項并刪除所有匹配項。從另一個列表中減去一個列表將刪除第二個列表中的每個項目。如果未找到匹配的項目,則會引發(fā)錯誤,因此您需要在刪除列表項之前,需要提前知道該列表項是否存在。
a = [ "first", "second", "third", "first" ]
b = a - [ "first" ] # [ "second", "third" ]
a -= [ "second" ] # [ "first", "third", "first" ]
鑒于無法測試列表項的添加引入,可以這樣使用:設(shè)置一個文件或標(biāo)志的主列表,然后根據(jù)各種條件刪除不適用于當(dāng)前版本的文件或標(biāo)志?!?注:這算是推薦做法,維護(hù)一個主列表,然后只做減法,排除不適合的列表項。這個和下文的GYP提供的建議一樣。這里讀起來有些奇怪。
在風(fēng)格上,更喜歡只添加到列表中,讓每個源文件或依賴項出現(xiàn)一次。這與Chrome團(tuán)隊過去為GYP提供的建議相反(GYP更愿意列出所有文件,然后基于條件刪除您不需要的文件)。
列表項獲取
列表支持從零開始的下標(biāo)來提取值:
a = [ "first", "second", "third" ]
b = a[1] # -> "second"
[] 運算符是只讀的,不能用于改變列表。其主要使用場景是當(dāng)外部腳本返回多個已知值,并且您想要提取它們時。
在某些情況下,覆蓋一個列表比追加到一個列表更容易。為了幫助滿足這種情況,將非空列表賦值給值為非空列表的變量,會產(chǎn)生錯誤。如果要繞過此限制,請首先將目標(biāo)變量賦值給一個空列表。如下:
a = [ "one" ]
a = [ "two" ] # Error: overwriting nonempty list with a nonempty list.
a = [] # OK
a = [ "two" ] # OK
(3)Conditionals條件
條件語句類似于 C語言,如下??梢栽诖蠖鄶?shù)情況下,使用條件語句。甚至可以把整個target目標(biāo)放在條件里,如果這些target只在特定的條件下才需要聲明。
if (is_linux || (is_win && target_cpu == "x86")) {
sources -= [ "something.cc" ]
} else if (...) {
...
} else {
...
}(4)Looping循環(huán)
您可以使用foreach循環(huán)訪問列表。這是不鼓勵的。構(gòu)建應(yīng)該做的大多數(shù)事情通常都可以在不這樣做的情況下來完成,如果你覺得有必要,這可能表明你在元構(gòu)建中做了太多的工作。
foreach(i, mylist) {
print(i) # Note: i is a copy of each element, not a reference to it.
}(5)Function calls函數(shù)調(diào)用
簡單的函數(shù)調(diào)用看起來像大多數(shù)其他語言:
print("hello, world")
assert(is_win, "This should only be executed on Windows")這些函數(shù)是內(nèi)置的,用戶無法定義新的函數(shù)。一些函數(shù)采用以下代碼塊括起來:{ }。
static_library("mylibrary") {
sources = [ "a.cc" ]
}大多數(shù)函數(shù)定義了目標(biāo)target。用戶可以使用下面討論的template模板機制定義這樣的新功能。
準(zhǔn)確地說,上面說的代碼塊{}作為函數(shù)參數(shù)來執(zhí)行函數(shù)的。大多數(shù)塊樣式的函數(shù)執(zhí)行代碼塊,并將生成的作用域做為供讀取的變量字典。
(6)Scoping and execution作用域與執(zhí)行
文件和函數(shù)調(diào)用后面跟的{}塊引入新的作用域。作用域是嵌套的。讀取變量時,將按相反的順序搜索包含作用域,直到找到匹配的名稱。變量寫入始終轉(zhuǎn)到最內(nèi)層的作用域。
除了最里面的作用域之外,無法修改任何封閉作用域。這意味著,例如,當(dāng)您定義target目標(biāo)時,您在塊內(nèi)執(zhí)行的任何操作都不會“泄漏”到文件的其余部分。
if/else/foreach語句,即使它們使用{}塊,也不會引入新的作用域,因此更改將保留在語句之外。
3、Naming things文件和目錄名稱
文件名和目錄名是字符串,被解釋為相對于當(dāng)前構(gòu)建文件的目錄。有三種可能的形式:
- 相對名稱:
"foo.cc"
"src/foo.cc"
"../src/foo.cc"
- 源樹絕對名稱:
"http://net/foo.cc"
"http://base/test/foo.cc"
- 系統(tǒng)絕對名稱(罕見,通常用于包含目錄):
"/usr/local/include/"
"/C:/Program Files/Windows Kits/Include"
4、Build configuration構(gòu)建配置
(1)Targets目標(biāo)
一個目標(biāo)target是構(gòu)建圖中的一個節(jié)點。它通常表示將生成的某種可執(zhí)行文件或庫文件。目標(biāo)依賴于其他目標(biāo)。內(nèi)置目標(biāo)類型如下所示??梢允褂妹頶n help
- action:運行腳本以生成文件。
- action_foreach:為每個源文件運行一次腳本。
- bundle_data:聲明數(shù)據(jù)以進(jìn)入 Mac/iOS 捆綁包。
- create_bundle:創(chuàng)建蘋果/iOS 捆綁包。
- executable:生成可執(zhí)行文件。
- group:引用一個或多個其他目標(biāo)的虛擬依賴關(guān)系節(jié)點。
- shared_library:共享庫.dll或 .so。
- loadable_module:僅在運行時可加載.dll或 .so。
- source_set:輕量級虛擬靜態(tài)庫(通常比真正的靜態(tài)庫更可取,因為它的構(gòu)建速度更快)。
- static_library:.lib 或 .a 文件(通??梢允褂靡粋€source_set替代)。
(2)Configs配置
Configs配置是命名對象,用于指定flags、include目錄和defines。它們可以應(yīng)用于目標(biāo)target并推送到依賴目標(biāo)。
要定義配置,示例如下:
config("myconfig") {
includes = [ "src/include" ]
defines = [ "ENABLE_DOOM_MELON" ]
}要將配置應(yīng)用于目標(biāo),可以這樣做:
executable("doom_melon") {
configs = [ ":myconfig" ]
}構(gòu)建配置文件通常會為target目標(biāo)指定包含默認(rèn)配置的列表。目標(biāo)可以根據(jù)需要向此列表中添加或刪除。因此,在實踐中,您通常會使用configs += ":myconfig"附加到默認(rèn)值列表中。有關(guān)如何聲明和應(yīng)用配置的詳細(xì)信息,請參閱gn help config。
(3)Public configs公共配置
一個target目標(biāo)可以將配置項應(yīng)用于依賴于它的其他target目標(biāo)上。最常見的示例是第三方目標(biāo),它需要一些定義define或包含頭文件的include目錄,才能正確編譯。您希望這些配置項既應(yīng)用于第三方庫本身的編譯,也應(yīng)用于使用該庫的所有目標(biāo)。
為此,您需要使用要應(yīng)用的配置項編寫一個配置config:
config("my_external_library_config") {
includes = "."
defines = [ "DISABLE_JANK" ]
}然后,此配置將作為“公共”配置添加到目標(biāo)中。它將既適用于目標(biāo),也適用于直接依賴于它的目標(biāo)。注:使用的配置項是public_configs。
shared_library("my_external_library") {
...
# Targets that depend on this get this config applied.
public_configs = [ ":my_external_library_config" ]
}反過來,依賴目標(biāo)可以通過將目標(biāo)添加為“公共”依賴項,將其向上推進(jìn)到依賴項樹的另一個級別。注:使用的配置項是public_deps。
static_library("intermediate_library") {
...
# Targets that depend on this one also get the configs from "my external library".
public_deps = [ ":my_external_library" ]
}(4)Templates模板
模板是 GN 重用代碼的主要方式。通常,模板會擴展一個或多個其他target目標(biāo)類型。
# Declares a script that compiles IDL files to source, and then compiles those
# source files.
template("idl") {
# Always base helper targets on target_name so they're unique. Target name
# will be the string passed as the name when the template is invoked.
idl_target_name = "${target_name}_generate"
action_foreach(idl_target_name) {
...
}
# Your template should always define a target with the name target_name.
# When other targets depend on your template invocation, this will be the
# destination of that dependency.
source_set(target_name) {
...
deps = [ ":$idl_target_name" ] # Require the sources to be compiled.
}
}
通常,模板定義將放在一個.gni文件中,用戶將導(dǎo)入該文件以查看模板定義:
import("http://tools/idl_compiler.gni")
idl("my_interfaces") {
sources = [ "a.idl", "b.idl" ]
}聲明模板會在此Scope作用域內(nèi)的變量周圍創(chuàng)建一個閉包。調(diào)用模板時,魔術(shù)變量invoker用于讀取Scope作用域外的變量。模板通常會將其感興趣的值復(fù)制到自己的Scope作用域內(nèi):
template("idl") {
source_set(target_name) {
sources = invoker.sources
}
}執(zhí)行模板時的當(dāng)前工作目錄將是調(diào)用的構(gòu)建文件的目錄,而不是模板源文件的目錄。因此,從模板調(diào)用程序傳入的文件將是正確的(這通常占模板中的大多數(shù)文件處理)。但是,如果模板本身具有文件(也許它會生成運行腳本的操作),則需要使用絕對路徑(“//foo/...”)來引用這些文件,以說明當(dāng)前目錄在調(diào)用期間是不可預(yù)測的。有關(guān)詳細(xì)信息和更完整的示例,請參閱gn help template幫助。
5、Other features其他特性
(1)Imports導(dǎo)入
您可以使用該import函數(shù)將.gni文件導(dǎo)入到當(dāng)前作用域中。這不是C意義上的包含。導(dǎo)入的文件將獨立執(zhí)行,生成的作用域?qū)?fù)制到當(dāng)前文件中(C當(dāng)包含指令出現(xiàn)時,在當(dāng)前上下文中執(zhí)行包含的文件)。這允許緩存導(dǎo)入的結(jié)果,并且還阻止了一些更“創(chuàng)造性”的包含使用,如多次包含的文件。
通常,.gni文件將定義構(gòu)建參數(shù)和模板。有關(guān)詳細(xì)信息,請參閱gn help import。
您的.gni文件可以通過在名稱中使用前置下劃線(如_this)來表明該臨時變量不會到導(dǎo)入文件使用。
(2)Path processing路徑處理
通常,您需要相對于其他目錄創(chuàng)建文件名或文件名列表。這在運行腳本時尤其常見,腳本是使用構(gòu)建輸出目錄作為當(dāng)前目錄執(zhí)行的,而構(gòu)建文件通常引用相對于其包含目錄的文件。
您可以使用rebase_path轉(zhuǎn)換目錄。有關(guān)更多幫助和示例,請參閱gn help rebase_path。將相對于當(dāng)前目錄的文件名轉(zhuǎn)換為相對于根構(gòu)建目錄的典型用法是:new_paths = rebase_path("myfile.c", root_build_dir)。
(3)Patterns模式
模式用于為自定義target目標(biāo)類型的一組給定輸入生成輸出文件名,并自動從列表值中刪除文件(請參見gn help filter_include和gn help filter_exclude)。
它們就像簡單的正則表達(dá)式。有關(guān)詳細(xì)信息,請參閱gn help label_pattern。
(4)Executing scripts執(zhí)行腳本
有兩種方法可以執(zhí)行腳本。GN 中的所有外部腳本都是Python編寫的。第一種方法是作為構(gòu)建步驟。這樣的腳本將接受一些輸入并生成一些作為構(gòu)建部分的輸出。調(diào)用腳本的目標(biāo)使用“action”目標(biāo)類型進(jìn)行聲明(請參見gn help action)。
執(zhí)行腳本的第二種方法是在構(gòu)建文件執(zhí)行期間同步執(zhí)行。在某些情況下,需要確定要編譯的文件集,或者獲取構(gòu)建文件可能依賴的某些系統(tǒng)配置。構(gòu)建文件可以讀取腳本的stdout標(biāo)準(zhǔn)輸出,并據(jù)此以不同的方式對其進(jìn)行操作。
同步腳本執(zhí)行由函數(shù)exec_script完成(有關(guān)詳細(xì)信息和示例,請參閱gn help exec_script)。由于同步執(zhí)行腳本需要暫停當(dāng)前構(gòu)建文件的執(zhí)行,直到Python進(jìn)程完成執(zhí)行,因此外部腳本的速度很慢,應(yīng)將其最小化。
為防止濫用,允許調(diào)用exec_script的文件可以在頂級.gn文件中列入白名單。更多信息參考gn help dotfile。
您可以同步讀取和寫入文件,這是不鼓勵的,但在同步運行腳本時偶爾是必需的。典型的用例是傳遞一個長度超過當(dāng)前平臺命令行限制的文件名列表。請參閱gn help read_file和gn help write_file了解如何讀取和寫入文件。如果可能的話,應(yīng)避免使用這些功能。
超過命令行長度限制的操作可以使用響應(yīng)文件來繞過此限制,而無需同步寫入文件。查閱gn help response_file_contents了解更多內(nèi)容。
6、小結(jié)
本篇,我們學(xué)習(xí)了GN語言語法與腳本操作,支持的變量類型,命名、構(gòu)建配置等等。
??想了解更多關(guān)于開源的內(nèi)容,請訪問:??
?? 開源基礎(chǔ)軟件社區(qū)??
??https://ost.??。
網(wǎng)頁標(biāo)題:[GN+Ninja學(xué)習(xí)0x03]GN語法與操作學(xué)習(xí)
分享URL:http://m.5511xx.com/article/dpijhcg.html


咨詢
建站咨詢
