Xcode構(gòu)建過程的后臺工作(WWDC2018字幕搬運)
Xcode構(gòu)建過程的后臺工作(二)clang構(gòu)建
Xcode構(gòu)建過程的后臺工作(三)swift構(gòu)建
Xcode構(gòu)建過程的后臺工作(四)鏈接
什么是構(gòu)建過程
在構(gòu)建app的時候逸尖,從源代碼和項目資源開始丁稀,到提供給客戶的打包文件或者上傳到AppStore溯警,要經(jīng)過很多步驟。你要編譯和鏈接源碼沮明,復(fù)制和處理資源,比如頭文件堕油、資源目錄和storyboard女气,最后是代碼簽名及自定義腳本或者make文件,比如給框架構(gòu)建API文件捣作、運行代碼檢查和驗證工具等等誉结。大多數(shù)任務(wù)在構(gòu)建過程中由命令行工具運行,比如Clang券躁,LD惩坑,AC工具,IB工具也拜,代碼符號等以舒。這些工具等執(zhí)行,需要一組特定的實參慢哈,以特定的順序蔓钟,基于Xcode項目配置。
$ swiftc -module-name PetWall -target arm64-apple-ios12.0 -swift-version 4.2 ...
$ clang -x objective-c -arch arm64 ... PetViewController.m -o PetViewController.o
$ ld -o PetWall -framework PetKit PetViewController.o ...
$ actool --app-icon AppIcon ... Assets.xcassets
$ ...
$ [thousands more]
構(gòu)建系統(tǒng)的作用卵贱,就是將每次構(gòu)建的任務(wù)的執(zhí)行部署自動化滥沫,由于任務(wù)數(shù)量成千上萬,構(gòu)建過程更是數(shù)不勝數(shù)键俱,依賴關(guān)系十分復(fù)雜兰绣,你肯定不想手動輸入,1天敲100遍命令编振,那就讓構(gòu)建系統(tǒng)幫你做狭魂。
構(gòu)建任務(wù)執(zhí)行順序
構(gòu)建任務(wù)的執(zhí)行順序取決于信息的依賴關(guān)系,就是任務(wù)党觅,任務(wù)需要的輸入雌澄,和任務(wù)生產(chǎn)的輸出。以編譯任務(wù)為例杯瞻,它需要輸入源代碼文件镐牺,如PetViewController.m,然后輸出目標(biāo)文件魁莉,比如PetViewController.o睬涧。
同樣,鏈接服務(wù)需要幾個目標(biāo)文件旗唁,這些文件由編譯器在上個任務(wù)中生成畦浓,再生成可執(zhí)行或lib文件,比如PetWall運行文件检疫,會存到.app資源文件包讶请。
你能看到信息的依賴關(guān)系,是順著下圖的走向屎媳,最終形成執(zhí)行順序夺溢。
現(xiàn)在大家關(guān)注下圖中的編譯任務(wù)论巍,很像川流不息的馬路,你看得到編譯任務(wù)在各自的路上并行運行互不干涉风响。因為鏈接器任務(wù)需要所有的其他輸入嘉汰,所以它要在最后一位。
通過依賴關(guān)系構(gòu)建系統(tǒng)
構(gòu)建系統(tǒng)的第一步是獲取構(gòu)建描述状勤,Xcode項目文件鞋怀,解析項目中的所有文件,目標(biāo)app和依賴關(guān)系持搜,構(gòu)建設(shè)置接箫。轉(zhuǎn)換成一個樹形結(jié)構(gòu)叫做定向圖,它顯示了所有的依賴關(guān)系朵诫,項目中的輸入和輸出文件辛友,以及處理他們的執(zhí)行任務(wù)。
然后低級執(zhí)行引擎會處理這張圖剪返,研究依賴關(guān)系废累,決定執(zhí)行哪個任務(wù),執(zhí)行順序是什么脱盲,以及哪些可以平行運行邑滨,然后繼續(xù)執(zhí)行任務(wù)。這里的低級執(zhí)行引擎是新構(gòu)建系統(tǒng)的叫做llbuild钱反,他是開源的掖看,用GitHub開發(fā)。如果對構(gòu)建系統(tǒng)有興趣面哥,請隨意研究哎壳,看看它如何工作。它的鏈接和另一個關(guān)于構(gòu)建系統(tǒng)的開源模塊會在最后提到尚卫。
現(xiàn)在講講已知的依賴關(guān)系归榕。由于你無法獲取太多的依賴關(guān)系信息,構(gòu)建系統(tǒng)在任務(wù)的執(zhí)行過程中可能會找到更多信息吱涉。比如clang編譯oc文件時會生成目標(biāo)文件刹泄,但是它也會生成另一個文件,其中包含一個列出源文件中頭文件的列表怎爵,那么下次構(gòu)建時特石,構(gòu)建系統(tǒng)會使用這個文件中的信息,以保證當(dāng)你你改了其中任何頭文件時會再次編譯源文件鳖链。
這里的關(guān)系路徑是PetController.h姆蘸,PetController.d,.m直到.o文件。
構(gòu)建系統(tǒng)的主要工作就是執(zhí)行任務(wù)乞旦。當(dāng)然項目越大,構(gòu)建時間越長题山。你肯定不想在每次運行的時候把所有任務(wù)都運行一遍兰粉。構(gòu)建系統(tǒng)實際上可以只執(zhí)行定向圖上的任務(wù)子集,基于你對于項目的更改顶瞳,對比你上次的構(gòu)建玖姑,我們稱之為累加構(gòu)建。
準(zhǔn)確的依賴關(guān)系十分重要慨菱,這樣累加構(gòu)建才能正確高效的工作焰络。下面看看哪些更改會影響構(gòu)建系統(tǒng),以及與累加構(gòu)建的關(guān)系符喝。
更改檢測和任務(wù)簽名
構(gòu)建過程中的每個任務(wù)都有相應(yīng)的簽名闪彼,類似于Hash,通過計算多個任務(wù)相關(guān)信息而得出协饲。這些信息包括任務(wù)輸入的統(tǒng)計信息畏腕,比如文件路徑和更改時間標(biāo)簽,運行命令的命令行指示茉稠,以及其他有關(guān)任務(wù)的元數(shù)據(jù)描馅,比如編譯器版本。構(gòu)建系統(tǒng)會追蹤當(dāng)前和之前的任務(wù)簽名而线,所以它知道每次構(gòu)建時是否需要重新運行任務(wù)铭污。如果某個任務(wù)的簽名與上次構(gòu)建時不同,它就會重新運行這個任務(wù)膀篮,如果相同就會跳過嘹狞,這就是累加構(gòu)建的概念。
如何利用構(gòu)建系統(tǒng)
我們大概了解了構(gòu)建過程的定義和流程誓竿,那么如何利用構(gòu)建系統(tǒng)呢刁绒?先回顧下基本知識。構(gòu)建系統(tǒng)按照一定順序執(zhí)行一系列任務(wù)烤黍,但要記得構(gòu)建過程以定向圖表示知市。我們不用擔(dān)心任務(wù)執(zhí)行的順序,這是構(gòu)建系統(tǒng)的工作速蕊。作為開發(fā)者嫂丙,我們需要考慮的是任務(wù)之間的依賴關(guān)系,讓構(gòu)建系統(tǒng)根據(jù)定向圖結(jié)構(gòu)決定最佳的執(zhí)行方法规哲。這樣構(gòu)建系統(tǒng)可以正確地給任務(wù)排序跟啤,可能的時候并行運行,以完全利用多核硬件
依賴關(guān)系的來源
對于某些任務(wù),依賴關(guān)系來自構(gòu)建系統(tǒng)自帶的數(shù)據(jù)隅肥。構(gòu)建系統(tǒng)自帶一些規(guī)則竿奏,比如編譯器,鏈接器腥放,資源目錄泛啸,storyboard處理器等等。這些規(guī)則定義了哪些是輸入文件秃症,和哪些是輸出文件候址。
還有目標(biāo)依賴關(guān)系(target dependencies),大致決定了目標(biāo)構(gòu)建順序种柑。有些時候岗仑,構(gòu)建系統(tǒng)可以編譯不同目標(biāo)和平行文件,之前的Xcode要構(gòu)建一個app就要完成整個app的構(gòu)建聚请,然后才能使用荠雕。Xcode 10的新構(gòu)建系統(tǒng)就要快得多,編譯源階段會提前開始驶赏,免費提供并行舞虱。但是,如果含有任何運行腳本階段母市,這些階段完成后矾兜,并行才能開始。有關(guān)依賴的還有隱形依賴關(guān)系(Implicit dependencies)患久,例如椅寺,如果您在鏈接庫中列出目標(biāo)時,在方案編輯器中啟用了二進制構(gòu)建階段和隱式依賴項(這是默認開啟的)蒋失,那么即使它未在目標(biāo)中列出返帕,構(gòu)建系統(tǒng)也將建立對該目標(biāo)的隱式依賴關(guān)系。
接下來是構(gòu)建階段依賴(Build phase dependencies)篙挽。在目標(biāo)編輯器里荆萤,你會看到幾個構(gòu)建階段,復(fù)制頭文件铣卡、編譯源链韭、復(fù)制資源包等等。這些任務(wù)與每個階段相關(guān)煮落,通常根據(jù)階段的排列順序按組運行敞峭。如果有更好的方案,構(gòu)建系統(tǒng)也許會忽略它蝉仇,例如第三方靜態(tài)庫階段旋讹,在編譯源之前殖蚕。注意有的時候構(gòu)建階段順序不對會導(dǎo)致問題或者構(gòu)建失敗,因此請確保了解您的依賴關(guān)系并驗證構(gòu)建階段是否正確沉迹。
還有scheme順序依賴睦疫。如果在scheme設(shè)置里開啟了并行構(gòu)建檢查,構(gòu)建性能會更好鞭呕,不用擔(dān)心目標(biāo)順序蛤育。但是如果關(guān)閉并行構(gòu)建,Xcode構(gòu)建目標(biāo)時會按照你排列的構(gòu)建行動順序逐個構(gòu)建琅拌,目標(biāo)依賴關(guān)系優(yōu)先級較高缨伊,優(yōu)先決定第一個構(gòu)建目標(biāo)摘刑,但Xcode會遵從這個排列进宝。這讓人躍躍欲試,因為它給出了可預(yù)期的構(gòu)建順序枷恕,即使依賴關(guān)系有誤党晋。但這樣會犧牲大量并行空間,延緩構(gòu)建速度徐块。所以我們推薦開啟并行構(gòu)建未玻,正確設(shè)置依賴關(guān)系,不要依賴排序胡控。
最后扳剿,依賴關(guān)系取決于開發(fā)者。你可以自定義shell腳本構(gòu)建階段或規(guī)則昼激,明確告訴系統(tǒng)輸入和輸出是什么庇绽,以避免重復(fù)運行不必要的腳本任務(wù),保證正確執(zhí)行順序橙困。你可以用運行腳本階段編輯器定義輸入和輸出瞧掺,這些文件路徑將作為環(huán)境變量在腳本中激活。不要依賴項目里目標(biāo)依賴關(guān)系的自動連接凡傅。Clang編譯器里有自動關(guān)聯(lián)功能辟狈,在構(gòu)建設(shè)置中自動使用關(guān)聯(lián)框架。讓編譯器自動鏈接框架對應(yīng)導(dǎo)入的模塊夏跷,不用在鏈接庫的構(gòu)建階段再明確表示哼转。但是要注意自動關(guān)聯(lián)不會在構(gòu)建系統(tǒng)層級建立依賴關(guān)系,所以它不能保證依賴的目標(biāo)在關(guān)聯(lián)之前已經(jīng)建好槽华。所以它只適用于STK平臺的框架释簿,例如Foundation或者UIKit,因為我們知道它們在構(gòu)建前就已經(jīng)存在硼莽。你自己項目里的目標(biāo)庶溶,要保證明確的庫依賴關(guān)系煮纵。你也許需要創(chuàng)建項目引用,將另一個Xcode項目拖放到項目文件導(dǎo)航偏螺,說明與其他項目的目標(biāo)文件的依賴關(guān)系行疏。
總結(jié)來說,有了準(zhǔn)確的依賴關(guān)系套像,構(gòu)建系統(tǒng)就能更好的并行構(gòu)建任務(wù)酿联,保證每次的構(gòu)建結(jié)果一致。這樣就能減少構(gòu)建用時夺巩,給開發(fā)多點時間贞让。想知道更多快速構(gòu)建的內(nèi)容,如何最大化利用新的iMac pro內(nèi)核柳譬,推薦觀看演講《用Xcode加速構(gòu)建過程》喳张。