Swift:編譯流程

原創(chuàng):知識點總結(jié)性文章
創(chuàng)作不易粥航,請珍惜,之后會持續(xù)更新冤狡,不斷完善
個人比較喜歡做筆記和寫總結(jié)孙蒙,畢竟好記性不如爛筆頭哈哈项棠,這些文章記錄了我的IOS成長歷程,希望能與大家一起進步
溫馨提示:由于簡書不支持目錄跳轉(zhuǎn)马篮,大家可通過command + F 輸入目錄標題后迅速尋找到你所需要的內(nèi)容

目錄

  • 一沾乘、LLVM
  • 二、編譯流程
  • 三浑测、 Package Manager

一翅阵、LLVM

在深入 SIL 之前,先簡單介紹 LLVM迁央,經(jīng)典的 LLVM 三段式架構(gòu)如下圖所示掷匠,分為前端(Frontend),優(yōu)化器(Optimizer)和后端(Backend)岖圈。當需要支持新語言時只需實現(xiàn)前端部分讹语,需要支持新的架構(gòu)只需實現(xiàn)后端部分,而前后端的連接樞紐就是 IR(Intermediate Representation)蜂科,IR 獨立于編程語言和機器架構(gòu)顽决,故 IR 階段的優(yōu)化可以做到抽象而通用。

Frontend

前端經(jīng)過詞法分析(Lexical Analysis)导匣,語法分析(Syntactic Analysis)生成 AST才菠,語義分析(Semantic Analysis),中間代碼生成(Intermediate Code Generation)等步驟贡定,生成 IR赋访。

IR

IR 是 LLVM 前后端的橋接語言,其主要有三種格式缓待,這三種格式完全等價:

  • 可讀的格式蚓耽,以.ll 結(jié)尾
  • Bitcode 格式,以.bc 結(jié)尾
  • 運行時在內(nèi)存中的格式
Optimizer

IR經(jīng)過優(yōu)化器進行優(yōu)化旋炒,優(yōu)化器會調(diào)用執(zhí)行各類 Pass步悠。所謂 Pass,就是遍歷一遍 IR瘫镇,在進行針對性的處理的代碼贤徒。LLVM 內(nèi)置了若干 Pass,開發(fā)者也可自定義 Pass 實現(xiàn)特定功能汇四,比如插樁統(tǒng)計函數(shù)運行耗時等。

Xcode Optimization Level

Xcode - Build Setting - Apple Clang - Code Generation - Optimization Level中踢涌,可以選定優(yōu)化級別通孽,-O0表示無優(yōu)化,即不調(diào)用任何優(yōu)化 Pass睁壁。其他優(yōu)化級別則調(diào)用執(zhí)行對應(yīng)的 Pass背苦。

Backend

后端將 IR 轉(zhuǎn)成生成相應(yīng) CPU 架構(gòu)的機器碼互捌。


二、Swift 編譯流程

Swiftc

不同于 OC 使用 clang 作為編譯器前端行剂,Swift 自定義了編譯器前端 swiftc秕噪,如下圖所示。這里就體現(xiàn)出來 LLVM 三段式的好處了厚宰,支持新語言只需實現(xiàn)編譯器前端即可腌巾。對比clang,Swift 新增了對 SIL(Swift Intermediate Language)的處理過程铲觉。SIL 是 Swift 引入的新的高級中間語言澈蝙,用以實現(xiàn)更高級別的優(yōu)化。

Swift 編譯流程

Swift 源碼經(jīng)過詞法分析撵幽,語法分析和語義分析生成 AST灯荧。SILGen 獲取 AST 后生成 SIL,此時的 SIL 稱為 Raw SIL盐杂。在經(jīng)過分析和優(yōu)化逗载,生成 Canonical SIL。最后链烈,IRGen 再將 Canonical SIL 轉(zhuǎn)化為 LLVM IR 交給優(yōu)化器和后端處理厉斟。


三、 Package Manager

前言

Swift Package Manager是 Apple 為了彌補當前 iOS 開發(fā)中缺少官方組件庫管理工具的產(chǎn)物测垛。相較于其他組件管理控件捏膨,他的定義文件更加輕松易懂,使用起來也很 Magic食侮,只需將源碼放入對應(yīng)的文件夾內(nèi)号涯,Xcode 就會自動生成工程文件,并生成編譯目標產(chǎn)物所需要的相關(guān)配置锯七。同時链快,SPM 與 Cocoapods 相互兼容,可以在特性上提供互補眉尸。這篇文章將主要介紹該組件管理器的現(xiàn)狀域蜗,常見使用方法,和將來的一些思考噪猾。

本人是十分喜歡Swift Package 的霉祸,本地集成方便快捷,也給我很大的權(quán)力讓我所想落實到幾乎不可能落實的上游倉庫袱蜡。配合 Swift Access Control丝蹭,例如 module 內(nèi)可訪問的 internal屬性,很大程度上解決了寫 App 后臺的時候被 UI 意外調(diào)用造成的 crash坪蚁,彌補上 Swift 沒有class-private 訪問控制關(guān)鍵字的遺憾奔穿。調(diào)試可以直接打到代碼上镜沽,速度也很快。如果能為 Package 提供 .patch的擴展文件贱田,再配合優(yōu)化后的遠端倉庫缅茉,這將很有可能取代臃腫的 Cocoapod。pod 會修改編譯目標的 xcconfig男摧,而 Swift Package 通過提供libraryworkspace 的集成方式蔬墩,侵入性非常低。最后彩倚,Swift Package 的多平臺編譯的能力也非常好筹我,UIKit 一次編寫即可適配 iOS/iPadOS/tvOS/watchOS,編譯配置 CI 只需要調(diào)用 xcodebuild 即可自動解析帆离,如有缺失自動拉取蔬蕊,省時省力。個人項目我可能不會再碰 Cocoapods哥谷。

開源組件使用情況

查看當前開源組件的 SPM 接入供應(yīng)情況岸夯,不難發(fā)現(xiàn)幾乎全部還在維護的框架都支持使用這種方式集成。大到微軟的 APM SDK们妥,小到界面 UI 組件猜扮,均有良好的兼容支持。下面列舉一些可能會在后續(xù)開發(fā)中用到的組件监婶。資源選自 https://github.com/ivanvorobei/awesome-ios旅赢。檢測規(guī)則為是否在倉庫主目錄下存在 Package.swift 文件。統(tǒng)計中惑惶,56%左右的框架已經(jīng)適配了 SPM 接入煮盼,且已經(jīng)開始出現(xiàn)如 MarkdownUI 等框架僅適配 SPM 的情況。

優(yōu)勢
  • 簡化的定義流程:將文件放入約定的目錄內(nèi)即可一鍵打包带污。
  • 簡化的 SPM 版本管理:Xcode 會根據(jù)定義文件首行說明自動查找兼容的解決方案僵控。
  • 簡化的上手流程:不需要安裝工具,也不需要命令行安裝組件鱼冀。
  • 良好的持續(xù)集成能力:在完成項目配置以后报破,xcodebuild 無縫銜接,自動拉倉千绪。
  • 良好的兼容性:可與現(xiàn)有的大多數(shù)組件管理方案混用充易。
  • 良好的調(diào)試能力:斷點快狠準。
缺點
  • 文檔難找荸型。
  • 使用遠端倉庫對網(wǎng)絡(luò)要求非常高蔽氨。
創(chuàng)建組件

創(chuàng)建組件可以在 Xcode 中選擇Swift Package,也可以在命令行中寫入 swift package init。命令行創(chuàng)建會將當前目錄名稱用作包名鹉究。

Creating library package: Desktop
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/Desktop/Desktop.swift
Creating Tests/
Creating Tests/DesktopTests/
Creating Tests/DesktopTests/DesktopTests.swift
定義組件
// swift-tools-version:5.5
import PackageDescription

let package = Package(
  name: "MyLibrary",
  products: [
    .library(name: "MyLibrary", targets: ["MyLibrary"]),
  ],
  dependencies: [ ],
  targets: [
    .target(name: "MyLibrary", dependencies: []),
    .testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),
  ]
)

請勿忽略swift-tools-version:5.5行,當打包編譯出現(xiàn)工具鏈版本不匹配踪宠、 SDK 版本自赔、系統(tǒng) API 最低版本等問題時需要首先到這里排查可能存在的問題。

Targets

先看 targets柳琢,定義是 A target can define a module or a test suite. 翻譯來說绍妨,就是一個 target 對應(yīng)一個 clang module 或者 一個測試目標。一個 target 內(nèi)只允許使用一類語言柬脸,比方說 Swift 或者 Objective-C/C/CPP他去。此處的name 只對當前 package 可見,可以填寫在任意一個 dependencies 內(nèi)倒堕。Target 支持 binary Target灾测,可使用 XCFramwork.a .so 等二進制。

Products

再看 products垦巴,定義是 Products define the executables and libraries a package produces, and make them visible to other packages. 一個 product 可以包含多個 target媳搪,他們會被編譯成產(chǎn)物提供給項目。如果其他項目依賴當前的 Swift Package骤宣,此處的 name 可以填寫入其他Package的依賴需求內(nèi)秦爆,一般對內(nèi)不可用。最后來看一下product 的幾種類型憔披。一般來說等限,常見的 .librarytype 包含 .static(默認) 和 .dynamic。除開.library還有 .executable可選芬膝,用于編譯測試用二進制和 macOS 命令行工具望门。

.library(name: "MorphingLabel", targets: ["MorphingLabel"]),
.library(name: "MorphingLabelDynamic", type: .dynamic, targets: ["MorphingLabel"]),

.executable(name: "appdecrypt", targets: ["appdecrypt"])
資源文件

Swift Package 需要對每一個文件指明用途。代碼文件會自動識別并編譯打包蔗候,資源文件需要指定和說明怒允。Swift Package 會為每一個 Package 生成一個 module 擴展,以便直接調(diào)用锈遥。使用命令行將項目文件 Package.swift 轉(zhuǎn)換成 xcproj 則不會生成該模版定義文件纫事。以下定義會在 Bundle 類內(nèi)生成.module屬性專門用于獲取 Particles 文件夾內(nèi)的資源。

.target(
    name: "MorphingLabel",
        exclude: ["Info.plist", "tvOS-Info.plist"],
        resources: [ .process("Particles") ] // <-- 資源文件
),
目錄結(jié)構(gòu)
  • Swift Package 推薦使用原生目錄結(jié)構(gòu)所灸,不推薦自定義 Path丽惶。
  • Swift Package 導(dǎo)出頭文件有規(guī)定的位置,在當前 Source Path 內(nèi)創(chuàng)建 include 會自動導(dǎo)出爬立。
  • Swift Package 需要對每一個資源文件/文件夾顯示聲明钾唬,對通配符的適配存在 Bug。
  • 當需要特定的文件目錄組織的時候可以使用 符號連接 來鏈接目標文件。

總體來說Swift Package中一個Target對應(yīng)一個 name抡秆,而 項目根目錄/Sources/name會作為當前Target的工作搜索路徑奕巍。

├── Package.swift        # 定義文件
├── README.md            # 可忽略
├── Sources              # 此文件夾內(nèi)全部文件都需要定義 不然會報錯
│  └── demo             # target demo 的默認目錄
│    ├── Particles.     # 在 target 內(nèi)聲明為資源文件
│    │  └── fire.png   # 會自動打包成 bundle 拷貝并傳遞
│    ├── demo.swift     # target demo 的項目源代碼
│    └── include        # 導(dǎo)出頭文件
│      └── export.h     # 頭文件

# 在 Sources / target name 厘頭的資源文件可在 Package.swift 內(nèi)定義。
# 需要在 target 內(nèi)添加 resources: [ .process("Particles") ]
XCFramework

關(guān)于編譯產(chǎn)物儒士,基礎(chǔ)的 Swift Package 可以生成靜態(tài)庫的止、動態(tài)庫,在這以后可以手動打包成 XCFramework着撩。SPM 的打包工作流對XCFramework 非常友好诅福,可以參考下面這個腳本。

xcodebuild -create-xcframework \
        -framework "$BUILD_FOLDER/iOS.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \
        -framework "$BUILD_FOLDER/tvOS.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \
        -framework "$BUILD_FOLDER/Simulator.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \
        -framework "$BUILD_FOLDER/tvOSSimulator.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \
        -output Build/LTMorphingLabel.xcframework

目前有針對 Package.swift 生成并編譯 XCFrameowrk 的懶人工具拖叙,但是由于其依賴將項目轉(zhuǎn)換成 xcproj 的編譯方法氓润,攜帶資源文件的 Swift Package 并不能用。

實踐

目前筆者有一個開源的私人項目使用了 SPM薯鳍,可以拉下倉庫來看一看圾旨。Xcode 在解析各種依賴方面并不穩(wěn)定盔然,所以項目采用的方案是將所有代碼拉到本地并通過修改dependencies 的方式采用本地解析集成齐板。本地集成的方式非常穩(wěn)定景鼠,而且最大程度的保證了你修改源碼的能力。Swift 發(fā)展非澈迹快悯舟,目前不推薦url直接集成遠端倉庫。

https://github.com/SailyTeam/Saily 在本地創(chuàng)建 xcworkspace以后便可以直接將 Package.swift 中的product添加到項目的編譯流程內(nèi)砸民。這里再次贊賞Swift Package的多元兼容抵怎,其中有一些庫是純 Objective-C 撰寫的,可以一鍵無縫集成岭参。

本地集成的其他好處自然也包含 0 編譯警告反惕,遇到任何問題你都可以直接打斷點到 Swift Package的代碼上。而 Cocoapod 經(jīng)常不靈演侯。關(guān)于編譯警告姿染,養(yǎng)眼準備!

其中可以重點關(guān)注幾個混合編譯的庫的定義和 Fluent Icon 庫的定義文件秒际。其中就如上面描述的一樣悬赏,include 文件會被自動導(dǎo)出給 Swift 使用。

常見問題

Q: 我導(dǎo)入了 Swift Package 到項目娄徊,但無法import
A: 請command + shift + K清理項目重新編譯闽颇。Swift Packagemodule緩存。

Q: 我的include指向上級目錄的頭文件寄锐,導(dǎo)出失敗了
A: 請清理項目重新編譯兵多,有時需要重啟 Xcode尖啡。

Q: 我在編譯的時候指定了最低要求 iOS 13,為何 Swift Package 無法調(diào)用 API剩膘?
A: 請檢查Package.swift是否有在platform內(nèi)指定版本衅斩,如有請升級 swift-tools-version 定義行。

Q: 我的資源文件在添加 process 以后仍然有警告
A: 請使用文件夾名字或指定每一個文件的名字援雇,通配符并不能很好的工作矛渴。

Q: 我在定義 Package.swift 的時候沒有找到你說的這個幾個字段
A: 請升級第一行的 swift-tools-version

Q: 聯(lián)網(wǎng)拉取 Swift Package 無法完成
A: 請考慮清除~/Library/Caches/org.swift.swiftpm/惫搏,并換個好一些的網(wǎng)絡(luò)。如果依然失敗請刪除 Package.resolved 文件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載蚕涤,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者筐赔。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市揖铜,隨后出現(xiàn)的幾起案子茴丰,更是在濱河造成了極大的恐慌,老刑警劉巖天吓,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贿肩,死亡現(xiàn)場離奇詭異,居然都是意外死亡龄寞,警方通過查閱死者的電腦和手機汰规,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來物邑,“玉大人溜哮,你說我怎么就攤上這事∩猓” “怎么了茂嗓?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長科阎。 經(jīng)常有香客問我述吸,道長,這世上最難降的妖魔是什么锣笨? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任蝌矛,我火速辦了婚禮,結(jié)果婚禮上票唆,老公的妹妹穿的比我還像新娘朴读。我一直安慰自己,他們只是感情好走趋,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布衅金。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪氮唯。 梳的紋絲不亂的頭發(fā)上鉴吹,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音惩琉,去河邊找鬼豆励。 笑死,一個胖子當著我的面吹牛瞒渠,可吹牛的內(nèi)容都是我干的良蒸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼伍玖,長吁一口氣:“原來是場噩夢啊……” “哼嫩痰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起窍箍,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤串纺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后椰棘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纺棺,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年邪狞,在試婚紗的時候發(fā)現(xiàn)自己被綠了祷蝌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡外恕,死狀恐怖杆逗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鳞疲,我是刑警寧澤罪郊,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站尚洽,受9級特大地震影響悔橄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腺毫,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一癣疟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧潮酒,春花似錦睛挚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侧到。三九已至,卻和暖如春淤击,著一層夾襖步出監(jiān)牢的瞬間匠抗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工污抬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留汞贸,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓印机,卻偏偏與公主長得像矢腻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子射赛,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容