聊聊Xcode 中的編譯過程以及編譯器

編譯過程

基本的編譯過程分爲四個步驟:html

  1. 預處理(Pre-process):把宏替換,刪除註釋,展開頭文件,產生 .i 文件。前端

  2. 編譯(Compliling):把以前的 .i 文件轉換成彙編語言,產生 .s文件。objective-c

  3. 彙編(Asembly):把彙編語言文件轉換爲機器碼文件,產生 .o 文件。後端

  4. 連接(Link):對.o文件中的對於其餘的庫的引用的地方進行引用,生成最後的可執行文件(同時也包括多個 .o 文件進行 link)。xcode

而後經過解析 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 之間的依賴關係。app

在 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 這個術語不能一律而論,具體區別的在這篇博客有講述。

若是對編譯器自己產生了興趣,能夠一方面能夠看看編譯原理(程序猿的三大浪漫之一),而後另外一方面能夠本身瞭解一個編譯器應該怎麼寫。
這裏有個知乎專欄
同時還有斯坦福大學的公開課供學習參考。

相關文章
相關標籤/搜索