iOS編譯過程,ios編譯
編譯過程
基本的編譯過程分為四個步驟:
然後通過解析 xcode 編譯 log,可以發現 xcode 是根據 target 分開進行編譯的。每個 target 的具體的編譯過程也可以通過展開 log 日誌獲得。基本的格式就是首先簡明一句說明要幹什麼,然後縮排的幾行說明具體的操作。比如:
(1) ProcessPCH /.../Pods-SSZipArchive-prefix.pch.pch Pods-SSZipArchive-prefix.pch normal armv7 objective-c com.apple.compilers.llvm.clang.1_0.compiler (2) cd /.../Dev/objcio/Pods setenv LANG en_US.US-ASCII setenv PATH "..." (3) /.../Xcode.app/.../clang (4) -x objective-c-header (5) -arch armv7 ... configuration and warning flags ... (6) -DDEBUG=1 -DCOCOAPODS=1 ... include paths and more ... (7) -c (8) /.../Pods-SSZipArchive-prefix.pch (9) -o /.../Pods-SSZipArchive-prefix.pch.pch
就是在處理 pch 標頭檔,首先切換到 pch 的目錄下,然後設定環境變數,然後啟動 clang 並進行一系列的配置。在這之後一般就會產生具體的 .o
檔案作為產出(一般是有多個,針對不同的平台架構分別有一個,不過一般緊接著會把這些彙總成一個通用的 library。)。同時注意,不同的 target 也是有編譯順序的,具體的要看 target 之間的依賴關係。
在 xcode 編譯的過程中,大部分的命令都可以自解釋,不過仍有個別的命令直接看是看不出來幹嘛的,這裡解釋一下:
ld
:用於產生可執行檔。
libtool
:產生 lib 的工具。
(這部分將會在之後的文章的編譯具體過程進行講解)
接下來就是編譯過程的控制,在 xcode 中可以通過 Build phases
,Build settings
以及 Build rules
來進行控制。
Build phases主要是用來控制從源檔案到可執行檔的整個過程的,所以應該說是面向源檔案的,包括編譯哪些檔案,以及在編譯過程中執行一些自訂的指令碼什麼的。
Build rules 主要是用來控制如何編譯某種類型的源檔案的,假如說相對某種類型的原檔案進行特定的編譯,那麼就應該在這裡進行編輯了。同時這裡也會大量的運用一些 xcode 中的環境變數,完整的官方文檔在這裡:Build Settings Reference
Build settings則是對編譯工作的細節進行設定,在這個視窗裡可以看見大量的設定選項,從編譯到打包再到程式碼簽署都有,這裡要注意 settings 的 section 分類,同時一般通過右側的 inspector 就可以很好的理解選項的意義了。
最後,要說一下我們的工程檔案.pbxproj
,以上的所有的這些選項都儲存在這個檔案中。當然也包括 target 的資訊,項目所有檔案的資訊,這個檔案是一個文字檔,可以用文字編輯器開啟。裡頭的內容基本是可讀性比較強的。基本的思路很物件導向,每個東西都有屬性,如果屬性是另一個對象,值就是那個對象的一個『引用』,就是一串數字(唯一的)作為表示。每個對象都有這樣的引用。
編譯器
首先,編譯器是做什麼的?編譯器是用來把原始碼檔案轉換為更為低級的語言的(同時還有語句的靜態分析),而 xcode 使用的clang 編譯器的作用就是把原始碼轉換為更為低級的 LLVM IR(Intermedia Representation),這個 LLVM IR 是作業系統無關的,然後 LLVM 通過這個中繼語言來進行下一步的二進位檔案的產出。得益於 LLVM 的三層架構,LLVM 可以有多個輸入和輸出(LLVM 的第一層架構是用於處理輸入的,第二層用於最佳化 IR ,第三層用於輸出)這裡遇到了一個問題,不瞭解到底 clang 和 LLVM 之間的關係是什麼,估計得明白編譯器是怎麼做的才能明白。
通常一個編譯器可以編譯多種語言,產生多個平台的代碼,所以會劃分前端和後端。有時候還有中端的說法。
前端是語言相關的,輸出為抽象文法樹;
後端是機器相關的,輸出為機器代碼。有些最佳化是機器無關的,這一部分可能被單列出來稱為中端。
以gcc為例,前端產生的中繼語言為GENERIC,之後轉化為gimple做機器無關的最佳化,最後轉化為RTL做機器相關最佳化並產生機器代碼。
這三個部分就可以分別稱為前端、中端、後端。不過gimple階段是gcc 4之後才有的,gcc 3.x的版本最佳化全在RTL上。
而且實際實現的時候可能機器相關的最佳化也在gimple階段實現(反過來RTL也有機器無關最佳化),劃分不是那麼明確。
也就是說前段完成文法分析句法分析等相關的工作,並不會針對機器平台做想對應的最佳化。後端才是真正蟾蜍機器碼的部分。clang 只是一個編譯器的前前端部分。而 LLVM 這個術語不能一概而論,具體區別的在這篇部落格有講述。
如果對編譯器本身產生了興趣,可以一方面可以看看編譯原理(程式猿的三大浪漫之一),然後另一方面可以自己瞭解一個編譯器應該怎麼寫。
這裡有個
申明:本文是為了本人更好的翻閱記憶,原文連結
其他相關文檔:
1、從零開始寫個編譯器
2、同時還有斯坦福大學的公開課和知乎專欄可以參考
3、用Xcode查看預先處理檔案方法