【譯】更好的瞭解Xcode構建系統

原文:medium.com/flawless-ap…前端

做者:Varun Tomarshell

一個程序在運行到一臺設備以前經歷了不少轉換的步驟。和其它的編程語言處理系統同樣,Xcode構建系統爲了確保執行順序和各類依賴庫,須要運行不少命令行指令,傳遞各類各樣的參數。整個構建過程分爲如下五個階段:編程

一、預處理swift

二、編譯後端

三、彙編xcode

四、連接bash

五、加載架構

預處理

預處理的目的是把咱們的程序轉換成能被編譯器識別的形式。它用宏的定義替換宏,發現依賴關係並解析預處理器指令。若是Swift編譯器沒有預處理器,咱們則不能在Swift項目中定義宏命令。在Xcode8中是容許咱們經過Build Setting中的SWIFT_ACTIVE_COMPILATION_CONDITIONS定義預處理器標記位的。它跟OC中的預處理器宏定義是一致的。app

編譯器

編譯是整個過程當中很是重要的一環。編譯器是一個程序,它把高級語言像是Swift&Objective C轉換成低級語言,像是目標文件。在iOS中有兩類編譯器『Clange & swiftc』。該過程能夠描述爲:less

注意:編譯器包含兩個主要部分:前端和後端。Clange是C/C++/Objective-C的編譯前端,swiftc是Swift的編譯前端,LLVM是後端。這些亂七八糟的東西是什麼,LLVM是從哪裏來的?

不要擔憂😉,我將會對它進行簡明扼要的介紹,儘管其中細節說明可能要單獨出一篇文章才能說明白(以後的文章,我會就這方面寫一篇文章)。

LLVM(Low Level Virtual Machine)是一個後端編譯器,用於在其上構建編譯器。它處理優化和生產適應目標架構(ARM、x86)的代碼。Clang/swiftc是一個前端編譯器,能夠解析C、C++、Objective C和Swift代碼,並將其轉換爲適合LLVM的中間表示(IR)。在本文後面,您將更好地理解它。

讓咱們更細粒度的理解下Swift語言的編譯過程,參考下面的圖示:

接下來介紹Swift編譯器示如何一步一步工做的:

一、Swift代碼解析成AST (Abstract Syntax Tree抽象語法樹)。AST是一個表明源碼結構的抽象語法樹,樹的每一個節點表明一個結構。它是什麼樣的呢,讓咱們經過一個例子來理解它。下面是咱們將要進行分析的Swift代碼:

//
// Test.swift
//
// Created by Varun Tomar
// Copyright © 2020 Varun Tomar. All rights reserved.
//

import Foundation

class Bird {
  func fly() { }
}

func isFlyHigh(bird: Bird) -> Bool { return false }

class Sparrow: Bird {
  override func fly() { }
  func add(x: Int, y: Int) -> Int { return x + y }
}
複製代碼

咱們能夠將這段代碼轉換成抽象語法樹格式內容,這須要運行

xcrun swiftc -dump-ast <filename>.swift
複製代碼

抽象語法樹輸出的結果頗有趣,這裏只展現了部分。若是你去閱讀這段輸出內容,會理解一些AST背後發生的事情。下面這段代碼展現了一些有意思的東西:

(func_decl range=[Test.swift:12:3 - line:12:16] "fly()" interface type='(Bird) -> () -> ()' access=internal
(parameter "self")
複製代碼

注意:如上面所示,當咱們建立一個函數時,swift會傳遞一個參數self,這就是爲何咱們能夠不傳入self直接訪問這些函數的緣由。

(class_decl range=[Test.swift:17:1 - line:20:1] "Sparrow" interface type='Sparrow.Type' access=internal non-resilient inherits: Bird
複製代碼

上面這段代碼展現了語法樹是如何描述繼承關係的。

二、如今咱們來到了AST構建完成以後的語義分析。語義分析負責將AST轉換爲格式漂亮,類型徹底檢查的AST格式。它移除了一些源碼語義問題上的警告和錯誤信息。

三、下一步是SIL的生成和優化。爲了在這個階段以後獲得SIL,咱們能夠運行:

xcrun swiftc -emit-silgen <filename>.swift
複製代碼

看到這些終端輸出的SIL生成內容,你可能會發出這樣的驚訝:"OMG😮。@$s4Test4BirdC3flyyyF是個什麼鬼東西?"。別擔憂,這沒你想的那麼可怕,它只是一種名稱混淆,用於將實體的附加信息壓縮到單個字符中。這個處理過的名稱包含了一些類型(class/struct/enum)、module、上下文等信息。例如,在@$s4Test4BirdC3flyyyF中,Bird後面的字母C表示Bird是一個class。它還能夠表達不少東西,但這不是本文的重點。若是你對它有興趣,能夠在反饋部分添加評論。此外咱們可使用swift-demangle追溯一個混淆的字符串直到它最初可讀的文本樣式。

可讀的SIL能夠經過以下命令實現:

xcrun swiftc -emit-silgen <filename>.swift | xcrun swift-demangle
複製代碼

咱們能夠看下它的結果:

此次它變得更易讀了。那就跟着我🚶‍♂️一塊兒探索SIL吧:

  • 函數是以關鍵詞nil開始的。
  • 關鍵詞hidden跟Swift代碼中的internal是對應的。
  • @main.Bird.fly() -> ()是從混淆的文本@$s4Test4BirdC3flyyyF中解析出來的,表明着函數名。
  • $@convention(method)意味着調用該函數須要一個上下文(context)。例如在self.fly()中,self就是函數調用的上下文。
  • $@convention(thin)表明着這是一個自由函數,它的調用不須要上下文。
  • 若是參數指定了類型,就須要owned的標記。

有沒有感受到頗有趣🧐?我其實有更有趣的發現。。。

四、在SIL的生成和優化以後,就來到了IR(中間件)階段。IR是LLVM的輸入內容。運行:

xcrun swiftc -emit-ir <filename>.swift
複製代碼

看到輸出的機器語言時,你可能會一臉懵逼🤯🤯

彙編

在這裏,控制由彙編程序來將輸入轉換爲可重定位的機器碼。它生成Mach-O文件。

Mach-O文件用於對象文件、可執行文件和庫。它是以一些有意義的字節碼組成的集合,將在iOS設備的ARM處理器或Mac設備的英特爾處理器上運行。

連接器和加載程序

連接器是一個將多個目標文件和庫合併爲一個Mach-O執行文件的程序。最後,操做系統中的加載器將程序裝載入內存並執行。

相關文章
相關標籤/搜索