Xcode的構建過程本質上是執行一系列構建任務。如:程式碼檢測,編譯程式碼,連結目標檔案,複製資源(圖片, plist, nib)檔案,程式碼簽名等。大部分任務是執行命令列工具,如(clang編譯、 ld連結、 codesign簽名, altool上傳)。這些工具使用xcode專案的配置資訊,根據特定的順序執行。bulid System的工作就是建立構建任務,並協調這些任務按照正確的順序執行。
構建任務的順序是任務之間的依賴關係定的。
如有2個類檔案,A檔案和B檔案, B檔案使用了A檔案的方法,引入了A檔案。那麼他們的編譯任務是編譯A,再編譯B,等都編譯好,將兩個.o檔案進行連結。
我們每次構建專案是對一個具體的 Target發起的,每個Target擁有自己的檔案和編譯規則,在專案裡可以存在多個子專案,這導致了在編譯的時候如果使用了 Cocoapods 或者擁有多個 target 的專案會先編譯依賴庫,然後再編輯當前專案。
如果使用的workspace開發,會遍歷裡面管理的專案,找到Target的依賴進行編譯,然後再編譯當前專案。這些依賴庫的Target的編譯過程和我們專案的編譯流程是一樣的。
Xcode編譯流程
編譯準備階段
1.建立編譯計劃,檢查依賴: 檢查當前專案中使用的依賴庫是否已經安裝並能夠被正確地引用;確定編譯順序,
如工程A下有子工程B,類C中引用了類D,要根據依賴關係建立編譯計劃,先編譯獨立的類,子專案;再編譯有依賴的類和主專案。
2.建立構建產物過程中和結束時需要的目錄
編譯階段
1.寫輔助檔案:建立app包目錄,後面編譯後的檔案都會被放入app包目錄中;將專案的檔案結構寫成.hmap對映檔案,方便後面引用查詢使用;
生成.hmap檔案:當編譯器處理.h檔案時,它會生成一個.hmap檔案,包含了所有的.h檔案及其對應的目標檔案的資訊。這些資訊將被用來加速下一次編譯過程。.hmap檔案通常被儲存在Derived Data目錄下。
2.執行預設指令碼:Cocoapods 會預設一些指令碼,當然你也可以自己預設一些指令碼來執行。這些指令碼都在 Build Phases 中可以看到;【'[CP] Check Pods Manifest.lock','Copy Pods Resources'】
3.編譯檔案:針對每一個檔案進行編譯,先編譯.Swift後編譯.m檔案,可以檢視每個檔案的編譯時間以及error和warning。
編譯原始碼檔案是編譯階段的重點,它會生成 Mach-O型別的.o檔案。這過程涉及到了 LLVM三相設計的 的完整流程:編譯前端、最佳化器、編譯後端。
4.連結檔案:將專案中的多個可執行檔案合併成一個檔案;連結分為動態連結和靜態連結,這2項也是程式設計師必須要面對的2個重要概念,這個原理衍生了很多重要的應用場景,如:iOS原生程式碼的熱更新,啟動效能最佳化,APP瘦身的符號剪裁等。
5.複製資原始檔:將專案中的資原始檔複製到目標app包;
6.編譯,連結 storyboard 檔案:storyboard 檔案也是會被編譯的;專案中的 storyboard 會被編譯成一個二進位制檔案,包含了介面的所有資訊和結構,這個檔案的字尾名為 .storyboardc。.storyboardc 檔案會在連結階段被連結到最終的應用程式中,以展示應用程式的使用者介面。
7.編譯 xib 檔案:。 xib 檔案會先解析成 XML 格式的檔案,然後編譯成二進位制格式的檔案,即 nib 檔案。編譯後的 nib 檔案會被打包到應用程式的 bundle 中。
8.編譯 Asset 檔案:我們的圖片如果使用 Assets.xcassets 來管理圖片,那麼這些圖片將會被編譯成機器碼,除了 icon 和 launchImage;
9.執行 Cocoapods 指令碼:將在編譯專案之前已經編譯好的依賴庫和相關資源複製到包中。
10.將 Swift 標準庫複製到包中
11.生成 .app 包
12.對包進行簽名
13.完成打包
XCode中專案的結構關係
Target:表示一種產物型別,它有很多設定可以做細節修改,如:Build Settings, Build Phases, Build Rules。
Configuration: 構建變體,如每個Target下預設就有2個構建變體Debug, Release。 這2個構建變體的設定內容都可以進行修改,如Deubeg下不產生DSYM符號檔案加速編譯,Release下產生DSYM符號檔案用於排查線上問題。
Scheme:產物生成驅動器,定義了產物生成的組成過程,Target和Configuaration是靜態的配置定義,Scheme是對這些定義的消費。
Project:是直接容器,它直接管理者程式碼檔案,資原始檔,指令碼檔案等。它可以引入其他Project作為它的依賴。
WorkSpace: 它是管理Project的專案容器,在它下面管理的Project可以透過依賴而使用,如A,B是WorkSpace下的兩個Project, A在它的Frameworks,Libraries..下點選“+”新增B的產物就可以使用了。如果使用cocopods時,這些會被自動做。
編譯性語言與解釋性語言
解釋性語言:邊解釋邊執行。特點是除錯快,執行慢。
直譯器在執行的時候把程式碼逐行做詞法分析,語法分析,語義分析,生成可執行的中間碼,然後執行中間碼。
編譯性語言:先編譯成可以在CPU上直接執行的機器碼,再執行。特點是執行快,除錯慢。
透過編譯器(採用三相設計:編譯器前端,LLVM IR中間碼最佳化器,編譯器後端),將程式碼字串做詞法分析,語法分析,語義分析,生成中間碼IR。
然後把IR中間碼交給最佳化器做最佳化。
IR最佳化器處理完後把結果交給編譯器後端編譯成不同架構(arm64, i386)的可執行檔案。
三相設計
假如有N種語言(C、OC、C++、Swift…)的前端,同時也有M個架構(模擬器、arm64、x86…)的Target,是否就需要 N × M 個編譯器?
三相架構的價值就體現出來了,透過共享最佳化器的中轉,很好的解決了這個問題。
假如你需要增加一種語言,只需要增加一種前端;假如你需要增加一種處理器架構,也只需要增加一種後端,而其他的地方都不需要改動。
LLVM的組成
在Xcode編譯iOS專案的時候,都是使用的LLVM,其實在編寫程式碼以及除錯的時候都在接觸LLVM提供的功能,例如:程式碼的亮度(Clang)、實時程式碼檢查(Clang)、程式碼提示(Clang)、debug斷點除錯(LLDB)。
LLVM專案是模組化和可重用的編譯器和工具鏈技術的集合。LLVM主要的子專案有一下幾個:
1.LLVM核心庫:
LLVM提供一個獨立的連結程式碼最佳化器,為許多流行CPU(以及一些不太常見的CPU)的程式碼生成支援。
這些庫是圍繞一個指定良好的程式碼表示構建的,稱為LLVM中間表示(“LLVM IR”)。
LLVM還可以充當JIT編譯器 - 它支援x86 / x86_64和PPC / PPC64程式集生成,並具有針對編譯速度的快速程式碼最佳化。
2.LLVM IR 生成器Clang:
Clang是LLVM的一個前端,它是LLVM的C / C ++ / Objective-C編譯器,旨在提供驚人的快速編譯(例如,在除錯配置中編譯Objective-C程式碼時比GCC快3倍),非常有用的錯誤和警告訊息以及提供構建優秀原始碼工具的平臺。
3.LLDB專案:
LLDB專案以LLVM和Clang提供的庫為基礎,提供了一個出色的本機偵錯程式。它使用Clang AST和表示式解析器,LLVM JIT,LLVM反彙編程式等,以便提供“正常工作”的體驗。在載入符號時,它也比GDB快速且記憶體效率更高。
4.lld專案:
lld專案旨在成為clang / llvm的內建連結器。目前,clang必須呼叫系統連結器來生成可執行檔案。
參考文章:
https://blog.csdn.net/dangyalingengjia/article/details/103336421
https://blog.csdn.net/Future_One/article/details/81882359
http://chuquan.me/2021/02/16/understand-ios-xcode-build-process/
https://xilankong.github.io/ios開發基礎/2020/07/29/Xcode-build過程都做了什麼.html