[GN+Ninja學(xué)習(xí) 0x05] GN編寫規(guī)范
??想了解更多關(guān)于開源的內(nèi)容,請?jiān)L問:??
OpenHarmony使用gn+ninja來維護(hù)開源項(xiàng)目的構(gòu)建。之前沒有接觸過gn+ninja,是時(shí)候系統(tǒng)性的來學(xué)習(xí)下了。邊學(xué)邊記錄下學(xué)習(xí)過程,希望對同樣需要學(xué)習(xí)gn+ninja的朋友有所幫助。
這一篇,我們來學(xué)習(xí)下GN的編寫規(guī)范,風(fēng)格指南,或者最佳實(shí)踐。也可以閱讀官方的英文原版內(nèi)容??docs/standalone.md??
1、Naming and ordering within the file文件中的命名和排序
(1)Location of build files構(gòu)建文件的位置
在靠近代碼之處創(chuàng)建更多的構(gòu)建文件比在頂層創(chuàng)建更少的構(gòu)建文件更有意義;這與我們對GYP所做的形成鮮明對比。這使得構(gòu)建文件、源文件更容易找到,需要檢視的內(nèi)容更是,因?yàn)楦募杏谔囟ǖ淖幽夸洝?- 注:所以使用gn,會看到較多的BUILD.gn。根目錄下有BUILD.gn,每個(gè)子模塊有BUILD.gn,通過依賴關(guān)系來調(diào)用。
(2)Targets
- 大多數(shù) BUILD.gn文件應(yīng)具有與目錄同名的目標(biāo)。此目標(biāo)應(yīng)為第一個(gè)目標(biāo)。
- 其他目標(biāo)應(yīng)該按照某種邏輯順序排列,通常更重要的目標(biāo)將排在第一位,單元測試目標(biāo)將緊跟相應(yīng)的功能模塊的目標(biāo)。如果沒有明確的順序,請考慮按字母順序排列。
- 測試支持庫應(yīng)為名為“test_support”的靜態(tài)庫,例如,“//ui/compositor:test_support”。測試支持庫應(yīng)包含非測試支持版本的庫作為公共依賴 public deps,以便測試程序只需要依賴于test_support目標(biāo)(而不是兩者)。
命名建議
- 目標(biāo)和配置應(yīng)使用小寫字母和下劃線分隔單詞來命名,除非有充分的理由不這樣做。
- 源代碼集source_set、組group和靜態(tài)庫static_library不需要名稱全局唯一。更喜歡為此類目標(biāo)提供簡短、非冗余的名稱,而不必?fù)?dān)心全局唯一性。例如,編寫依賴項(xiàng)"http://mojo/public/bindings"看起來比編寫依賴項(xiàng)"http://mojo/public/bindings:mojo_bindings"要好得多。
- 共享庫shared_library(以及擴(kuò)展的組件components–注:Chrome項(xiàng)目里有這樣的components 目標(biāo))必須具有全局唯一的輸出名稱。為此類目標(biāo)提供上面的簡短非唯一名稱,然后為該目標(biāo)提供全局唯一名稱output_name。
- 應(yīng)為可執(zhí)行文件和測試指定一個(gè)全局唯一的名稱。從技術(shù)上講,只有輸出名稱必須是唯一的,但由于只有輸出名稱出現(xiàn)在 shell 和bots中,因此如果名稱與可執(zhí)行文件出現(xiàn)的其他位置匹配一致,則迷惑性要小得多。
(3)Configs配置
- 與單個(gè)target目標(biāo)關(guān)聯(lián)的config配置應(yīng)與target目標(biāo)同名,并用?
?_config?
?跟在它后面。如 target名稱為foo,則對應(yīng)的config名稱為foo_config。 - config配置應(yīng)緊挨著使用它的相應(yīng)target目標(biāo)之前出現(xiàn)。
(4)Example
示例src/foo/BUILD.gn如下:
2、Ordering within a target目標(biāo)內(nèi)的排序
在一個(gè)target目標(biāo)內(nèi),推薦使用如下的排序,輸出在前,依賴在后,參與構(gòu)建的內(nèi)容在中間。
- output_name / visibility / testonly
- sources
- cflags, include_dirs, defines, configs,這幾個(gè)可以根據(jù)情況排序
- public_deps
- deps
(1)Conditions
僅影響一個(gè)變量的簡單條件(例如,添加單個(gè)源或?yàn)橐粋€(gè)特定操作系統(tǒng)添加標(biāo)志)可以位于它們影響的變量之下。影響范圍較多的更復(fù)雜的條件應(yīng)該在底部。
編寫條件時(shí),應(yīng)最大程度地減少條件塊的數(shù)量。
3、Formatting and indenting 格式化和縮進(jìn)
GN 包含一個(gè)內(nèi)置的代碼格式化程序,用于定義格式設(shè)置樣式。一些額外的說明:
- 變量為小寫帶下劃線分割單詞lower_case_with_underscores
- 注釋應(yīng)該是完整的句子,末尾有句點(diǎn)。
- 編譯器選項(xiàng)標(biāo)志等應(yīng)始終注釋它們的作用以及需要標(biāo)志的原因。
(1)Sources源文件
優(yōu)先僅列出一次源文件。可以有條件地包含源文件,而不是將它們?nèi)苛性陧敳浚缓笤谒鼈儾贿m用時(shí)有條件地排除它們。條件包含通常更清晰,因?yàn)槲募H列出一次,并且在閱讀時(shí)更容易推理。
(2)Deps依賴
- 依賴應(yīng)按字母順序排列。
- 當(dāng)前文件中的 Deps 應(yīng)首先寫入,并且不能使用文件名限定(僅需要:foo )。
- 其他 deps 應(yīng)始終使用完全限定的路徑名,除非出于某種原因需要相對路徑名。
(3)Import導(dǎo)入
使用完全限定的路徑進(jìn)行導(dǎo)入:
4、Usage用法
這一節(jié)介紹下如何選擇使用不同的target目標(biāo)類型,主要是source_set、shared_library、loadable_module,以及Components(這是Chrome項(xiàng)目定義的模板,可以忽略)。
(1)Source sets versus static libraries source_set與static_library的對比
在大多數(shù)情況下,源代碼集和靜態(tài)庫可以互換使用。如果您不確定要使用什么,則source_set幾乎永遠(yuǎn)不會出錯(cuò),并且不太可能引起問題,但是在大型項(xiàng)目中使用正確類型的target目標(biāo)類型可能很重要,因此您應(yīng)該了解以下權(quán)衡。
靜態(tài)庫static_library遵循不同的鏈接規(guī)則。當(dāng)鏈接靜態(tài)庫時(shí),只有包含未解析符號的對象文件才會被引入構(gòu)建中。source_set則會鏈接每個(gè)對象文件,都添加到最終二進(jìn)制文件中。
- 如果最終將代碼鏈接到組件component、共享庫或可加載模塊中,通常需要使用source_set。這是因?yàn)闆]有從共享庫中引用的符號的對象文件將根本不鏈接到最終庫中。即使該對象文件具有標(biāo)記為導(dǎo)出的符號,該符號的目標(biāo)依賴于該共享庫的需求,也會發(fā)生此遺漏。這將在鏈接后續(xù)目標(biāo)時(shí)導(dǎo)致未定義的符號。
- 單元測試(以及具有副作用的靜態(tài)初始值設(shè)定項(xiàng)的任何其他內(nèi)容)必須使用源代碼集source_set。gtest TEST 宏創(chuàng)建注冊測試的靜態(tài)初始值設(shè)定項(xiàng)。但是,由于沒有代碼引用對象文件中的符號,因此將測試鏈接到靜態(tài)庫,然后鏈接到測試可執(zhí)行文件意味著測試將被剝離。
- 在某些平臺上,靜態(tài)庫可能涉及復(fù)制組成它的對象文件中的所有數(shù)據(jù)。這會占用更多的磁盤空間,對于具有非常大對象文件的配置中的某些非常大的庫,可能會導(dǎo)致超出靜態(tài)庫大小的內(nèi)部限制。源集沒有此限制。某些目標(biāo)根據(jù)生成配置在源集和靜態(tài)庫之間切換,以避免此問題。一些平臺(或工具鏈)可能支持一種稱為“薄檔案”的東西,它沒有這個(gè)問題;但你不能依賴它作為便攜式解決方案。
- source_set可以沒有源文件,而靜態(tài)庫如果沒有源文件,則會給出特定于平臺的奇怪錯(cuò)誤。如果目標(biāo)只有頭文件(用于包括檢查目的)或在某些平臺上有條件地沒有源文件,請使用source_set。
- 在特定鏈接不需要大量符號的情況下(在鏈接測試二進(jìn)制文件時(shí)尤其如此),將該代碼放在靜態(tài)庫static_library中可以顯著提高鏈接性能。這是因?yàn)殒溄硬恍枰膶ο笪募囊婚_始就不會被考慮在內(nèi),而不是強(qiáng)迫鏈接器在以后的傳遞中去除未使用的代碼,因?yàn)闆]有代碼引用它。
(2)Components versus shared libraries versus source sets 組件、共享庫和source_set的比對
組件是Components Chrome 模板(而不是內(nèi)置的 GN 概念),不再贅述。
就像源代碼集與靜態(tài)庫權(quán)衡一樣,對于何時(shí)應(yīng)該使用組件,沒有硬性規(guī)定。使用組件可以顯著加快鏈接速度,從而顯著加快增量構(gòu)建速度,但它們要求您考慮需要從目標(biāo)導(dǎo)出哪些符號。
(3)Loadable modules versus shared libraries 可加載模塊和共享庫的比對
共享庫shared_library會被列在依賴它的target目標(biāo)的鏈接行上,并在應(yīng)用程序啟動和符號自動解析時(shí)由操作系統(tǒng)自動加載。可加載模塊loadable_module不會直接鏈接,應(yīng)用程序必須手動加載它。
在 Windows 和 Linux 上,共享庫和可加載模塊會產(chǎn)生相同類型的文件(分別是.dll和 .so)。唯一的區(qū)別是它們?nèi)绾捂溄拥揭蕾囁哪繕?biāo)上。在這些平臺上,可加載模塊的??deps?
?依賴與共享不會進(jìn)行鏈接的??data_deps?
?依賴是相同的。
在Mac上,這些target目標(biāo)具有不同的格式:共享庫將生成.dylib文件,可加載模塊將生成.so文件。
將可加載模塊loadable_module用于插件等等。對于類似插件的庫,最好同時(shí)為目標(biāo)類型使用可加載模塊(即使對于無關(guān)緊要的平臺),并為依賴于它的目標(biāo)使用data_deps,以便從兩個(gè)地方都清楚地知道庫將如何鏈接和加載。
5、Build arguments構(gòu)建參數(shù)
(1)Scope作用域
構(gòu)建參數(shù)的作用域應(yīng)限定在一個(gè)行為單元,例如啟用功能。通常,將在導(dǎo)入的文件中聲明一個(gè)參數(shù),以將其與可以使用它的構(gòu)建子集共享。
Chrome項(xiàng)目相關(guān)的一些風(fēng)格指導(dǎo)可以自行查看原文,基本上不需要了解。
(2)Type類型
參數(shù)支持所有GN語言的類型,字符串、列表、條件、循環(huán)、作用域等。
在絕大多數(shù)情況下,布爾值類型boolean是首選類型,因?yàn)榇蠖鄶?shù)參數(shù)都啟用或禁用功能或包含。
String字符串類型通常用于文件路徑。字符串也用于枚舉,盡管有時(shí)也使用整形integer。
(3)Naming conventions命名約定
雖然圍繞參數(shù)命名沒有硬性規(guī)定,但有許多共同的約定。如果要查看當(dāng)前構(gòu)建目錄的參數(shù)名稱和默認(rèn)值的參數(shù)列表,請使用gn args out/Debug --list --short。
- use_foo- 指示要包含的依賴項(xiàng)或主要代碼路徑(例如use_open_ssl,use_ozone,use_cups)
- enable_foo- 表示要啟用的功能或工具(例如enable_google_now,enable_nacl,enable_remoting,enable_pdf)
- disable_foo - 不建議使用,請使用enable_foo-,然后改變其默認(rèn)值
- is_foo- 通常是全局狀態(tài)描述符(例如is_chrome_branded,is_desktop_linux); 非全局變量的不推薦使用
- foo_use_bar- 前綴可用于指示參數(shù)的限制的作用域(例如,rtc_use_h264,v8_use_snapshot)
(4)Variables變量命名約定
在.gni文件中的頂級局部變量前面加上下劃線前綴。此前綴會導(dǎo)致變量無法被導(dǎo)入到其他構(gòu)建文件。
6、小結(jié)
本篇,我們學(xué)習(xí)了GN的編寫規(guī)范,風(fēng)格指南,包含命名、排序,格式化和縮進(jìn),用法,格式參數(shù)等的推薦用法。