自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Monorepo 解決方案 — 基于 Bazel 的 Xcode 性能優(yōu)化實(shí)踐

人工智能
在頭條工程切換至 Bazel 構(gòu)建系統(tǒng)后,為了支持用戶使用 Xcode 開發(fā)的習(xí)慣,我們使用了開源項(xiàng)目 Tulsi 作為生成工具,用于將 Bazel 工程轉(zhuǎn)換為 Xcode 工程。

背景介紹

書接上回《Monorepo 解決方案 — Bazel 在頭條 iOS 的實(shí)踐》,在頭條工程切換至 Bazel 構(gòu)建系統(tǒng)后,為了支持用戶使用 Xcode 開發(fā)的習(xí)慣,我們使用了開源項(xiàng)目 Tulsi 作為生成工具,用于將 Bazel 工程轉(zhuǎn)換為 Xcode 工程。但是在使用的過程中,我們發(fā)現(xiàn)了一些問題,其中影響較大的是,

  • Xcode 工程卡頓:對(duì)于頭條這種大型項(xiàng)目來說,Xcode 卡頓一直是本地研發(fā)的痛點(diǎn)問題,在切換 Bazel 構(gòu)建系統(tǒng)之后卡頓現(xiàn)象明顯加劇。
  • Xcode 功能支持受限:Tulsi 支持的功能有限,很多功能都年久失修,并未持續(xù)適配 Bazel 與 rules 的更新。

在做了一些前期調(diào)研后,我們發(fā)現(xiàn) rules_xcodeproj 提供了更好的解決方案。rules_xcodeproj 是一個(gè)由一系列 Bazel rules 組成的開源項(xiàng)目,使用它可以從一個(gè) Bazel 工程生成對(duì)應(yīng)的 Xcode 工程,實(shí)現(xiàn)在 Xcode 中寫代碼同時(shí)使用 Bazel 進(jìn)行真正的構(gòu)建任務(wù),相比于 Tulsi,rules_xcodeproj 在以下幾個(gè)方面有著更為明顯的優(yōu)勢(shì)。

  • Xcode 工程更加流暢: 頭條工程遷移到 rules_xcodeproj 后,工程首次冷啟、二次啟動(dòng)和文件增刪操作的時(shí)間有了明顯縮短,工程卡頓情況也明顯好轉(zhuǎn)。
  • Xcode 功能支持度更高: rules_xcodeproj 對(duì) Xcode 的支持更全面(包括單元測(cè)試、SwiftUI Previews),能夠更好地滿足我們的需求。
  • 社區(qū)更加活躍:隨著 rules_xcodeproj 在 2023 年 2 月份發(fā)布正式版,Tulsi 項(xiàng)目也正式宣布停止維護(hù),這意味著對(duì)于新版本的 Bazel,Tulsi 將不再提供適配和支持;同時(shí) rules_xcodeproj 幾乎每月都會(huì)更新一個(gè)中版本,對(duì)于后續(xù)適配 Bazel 更新的成本會(huì)更低。
  • 更符合 BitSky 的演進(jìn)路線:由 Bazel 驅(qū)動(dòng)的工程生成方式更符合 BitSky 的 Bazel Native 演進(jìn)路線,可以完全在 Bazel 環(huán)境下生成工程。

因此,我們將 Xcode 生成工具從 Tulsi 遷移到了 rules_xcodeproj,并對(duì) BitSky 工具鏈進(jìn)行了適配。適配過程中,我們?cè)谛迯?fù) rules_xcodeproj 索引問題的同時(shí),對(duì)工程結(jié)構(gòu)進(jìn)行了一些優(yōu)化,進(jìn)一步提升了工程流暢度。頭條 iOS 工程的測(cè)試數(shù)據(jù)如下:

測(cè)試設(shè)備 MacBook Pro,芯片 M1 Pro,內(nèi)存 32GB

ps. 本文介紹均基于 rules_xcodeproj 1.4.0 版本,build with bazel 模式

pps. 由于 Xcode 主線程的卡頓較難監(jiān)測(cè),此處用主動(dòng)操作的執(zhí)行時(shí)間衡量流暢度


Tulsi

rules_xcodeproj

工程首次冷啟

47s

16s

二次啟動(dòng)

33s

12s

文件操作

新增 20s

刪除 23s

新增 8s

刪除 6s

下面來看下我們具體做的適配工作。

適配過程

從 Tulsi 遷移到 rules_xcodeproj 后,我們發(fā)現(xiàn) Xcode 卡頓有明顯改善,仔細(xì)分析發(fā)現(xiàn) Tulsi 工程的卡頓主要有兩方面原因:

  1. 頭條工程中組件數(shù)量多、依賴關(guān)系復(fù)雜:Tulsi 的索引方案需要 Xcode Target 之間保留這些依賴關(guān)系,但這些依賴關(guān)系并不會(huì)被 Xcode 構(gòu)建消費(fèi),卻增加了 Xcode 對(duì)工程進(jìn)行解析和計(jì)算的成本。

圖中 Pod X_A 與 Pod X_B 為 Pod X 分別在 App A 與 App B 下被引用的 Target

圖片

  1. 全源碼構(gòu)建:我們?cè)谇袚Q Bazel 時(shí)還推進(jìn)了工程的全源碼構(gòu)建,源碼數(shù)量大幅增加,也給索引任務(wù)帶來更大的壓力。

接下來詳細(xì)說明整個(gè)分析和適配過程,帶大家更全面地了解我們結(jié)合 rules_xcodeproj 在 Xcode 背后做了什么。

在 Xcode 中開發(fā)時(shí)主要會(huì)用到三部分功能:構(gòu)建、索引和調(diào)試。而 Xcode 并不能直接理解 Bazel 項(xiàng)目的工程文件(BUILD 和 WORKSPACE),所以我們需要通過工具(rules_xcodeproj 或 Tulsi)將 Bazel 的工程文件轉(zhuǎn)換為 Xcode 可以理解的 .xcodeproj來支持這些功能正常工作。

各功能的適配點(diǎn)如下表所示。

圖片

rules_xcodeproj 原生方案的調(diào)試模塊基本能正常工作,而索引和構(gòu)建兩個(gè)模塊中我們對(duì)原生方案的改造較大,因此本文主要對(duì)這兩個(gè)模塊進(jìn)行展開介紹。

索引

前面提到支持 Xcode 索引功能需要提供各個(gè)源文件的編譯參數(shù),rules_xcodeproj 實(shí)現(xiàn)這一點(diǎn)的工作流程是:

  1. rules_xcodeproj 在 Bazel 的分析階段獲取到源碼文件和編譯參數(shù);
  2. 用這些信息創(chuàng)建 Xcode Target 的 Compile Sources 和 Build Setting 產(chǎn)出工程文件 .xcodeproj;
  3. Xcode 加載工程文件,獲取源碼文件對(duì)應(yīng)的索引參數(shù),調(diào)用 clang 或 swiftc 執(zhí)行索引命令。

遷移過程中我們注意到 rules_xcodeproj 工程的索引在多 Target 共用源文件的場(chǎng)景存在語法高亮異常的問題,對(duì)比 Tulsi 工程的處理方式后我們得出兩個(gè)結(jié)論:

  • 索引異常是由于 rules_xcodeproj 移除了所有 Library Target 間的依賴導(dǎo)致的;
  • Tulsi 工程中 Library Target 間的依賴關(guān)系正是導(dǎo)致 Tulsi 工程更為卡頓的關(guān)鍵原因。

后文會(huì)先分析依賴關(guān)系在索引中發(fā)揮的作用,以及 rules_xcodeproj 如何處理依賴關(guān)系移除帶來的問題,然后介紹我們?nèi)绾瓮ㄟ^源碼合并方案解決 rules_xcodeproj 索引方面的缺陷。

依賴關(guān)系在索引中的作用

這里提到的“依賴關(guān)系”主要是指 Xcode 工程文件(.xcodeproj,準(zhǔn)確來說是其中的project.pbxproj文件)中記錄的 Xcode Target 之間的依賴關(guān)系。

需要明確的是這部分依賴關(guān)系只會(huì)在 Xcode 索引過程被用到,被移除后也只會(huì)影響 Xcode 的索引功能。構(gòu)建時(shí)用到依賴關(guān)系 Bazel 會(huì)從 BUILD 文件中獲取,不會(huì)關(guān)心 .xcodeproj 中的信息。

這些依賴關(guān)系在 Xcode 中的表現(xiàn)形式如下圖所示,既有直接在 Build Phases 的 Target Dependencies 中聲明的依賴,也會(huì)對(duì)聲明在 Link Binary With Libraries 中 Target 產(chǎn)生依賴。

圖片

概括來說依賴關(guān)系在 Xcode 的索引過程中發(fā)揮著以下兩方面作用:

  • 正確的中間產(chǎn)物生成順序:clang/Swift Module 中間產(chǎn)物的生成需要依賴關(guān)系來確定構(gòu)建順序;
  • 正確的多 Target 編譯參數(shù):多 Target 共用源文件時(shí)應(yīng)用正確的編譯參數(shù),以便于正確地高亮代碼。

下面分別對(duì)其進(jìn)行展開介紹。

中間產(chǎn)物生成順序

以一個(gè) Swift 組件為例,當(dāng)它被 OC 組件引用時(shí),需要生成一個(gè) XX-Swift.h 文件,把方法和聲明暴露給 OC 組件,當(dāng)它被其他 Swift 組件引用時(shí),也需要生成一個(gè) swiftmodule 文件供其他 Swift 組件引用。XX-Swift.h 和 swiftmodule 并不是原始的源碼文件,也不是最終的二進(jìn)制產(chǎn)物,是構(gòu)建時(shí)的中間產(chǎn)物。在 Xcode 對(duì)一個(gè)組件的源代碼索引時(shí),需要這個(gè)組件的依賴組件已經(jīng)準(zhǔn)備好中間產(chǎn)物供索引時(shí)消費(fèi)。這里,我們先分析下 Xcode 是怎樣解決這個(gè)問題的。

首先,Xcode 通過 Target 來描述產(chǎn)物是如何構(gòu)建出來的,每個(gè) Target 擁有自己的 Build Phases  Build Settings(通常一個(gè)組件對(duì)應(yīng)一個(gè) Target)。Build Phases 中的 Compile Sources 記錄了構(gòu)建這個(gè) Target 需要編譯哪些源碼文件。Build Settings 里記錄了構(gòu)建這個(gè) Target 時(shí)的各種配置,這其中就包括了編譯參數(shù)。Xcode 可以從 Build Settings 里去獲取編譯參數(shù),然后對(duì) Compile Sources 中記錄的源碼文件進(jìn)行索引編譯。

圖片

一個(gè) Target 引用另外一個(gè) Target 時(shí),需要將依賴關(guān)系添加到 Build Phases  Target Dependencies 中。Xcode 在構(gòu)建時(shí)會(huì)根據(jù)這些依賴關(guān)系來決定構(gòu)建 Target 的順序,保證一個(gè) Target 構(gòu)建時(shí),它依賴的 Target 已經(jīng)完成構(gòu)建。

索引時(shí)也是類似的處理,Xcode 有一個(gè) Index Build 的階段,在這個(gè)階段也會(huì)根據(jù)依賴關(guān)系按照順序去觸發(fā) Target 的 Build Phase,完成之后才會(huì)開始索引這個(gè) Target 的源碼文件。

圖片

這里有一個(gè)例子,SwiftDemo 依賴了 HelloLib,兩個(gè) Target 均為 Swift 組件。

圖片

在對(duì) SwiftDemo 的源碼文件進(jìn)行索引編譯之前,會(huì)先觸發(fā) Index Build。Index Build 時(shí)根據(jù)依賴關(guān)系,先 Build HelloLib,這個(gè)時(shí)候會(huì)進(jìn)行 Compile Swift source files,參數(shù)中包含 -emit-module -emit-module-path /path,最終會(huì)生成 swiftmodule 。

圖片

如果將 HelloLib 從 SwiftDemo 的 Target Dependencies 中移除,在 SwiftDemo 的 Index Build 時(shí),不會(huì)先 Build HelloLib,并且在 HelloLib 的 Index Build 中,也不會(huì)生成 swiftmodule。

可以看出,Xcode 正是通過依賴關(guān)系來保證構(gòu)建時(shí)的順序,索引也依賴構(gòu)建順序來保證 Module 中間產(chǎn)物的生成時(shí)機(jī)。

在 Tulsi 生成的 Xcode 工程中,依然保留了 Target 之間的依賴關(guān)系,用于解決索引的中間產(chǎn)物生成問題。這里就不做過多介紹。

而在 rules_xcodeproj 生成的工程中,Target 之間的依賴關(guān)系是完全去掉的,每個(gè) Target 都有且僅有一個(gè)額外添加的依賴 BazelDependencies。對(duì)于 Module 中間產(chǎn)物的處理,在生成的 Xcode Target 中,我們可以看到這樣一條 Build Phase

圖片

原理是在 Index Build 階段,執(zhí)行到這 Target 時(shí)去跑一個(gè) shell 腳本。

以 NewsInHouse 這個(gè) Target 為例,這個(gè)腳本里經(jīng)過一系列的處理,最終會(huì)去調(diào)用這樣一條 Bazel Build 命令,

圖片

在這條命令中有一些關(guān)鍵信息:

@rules_xcodeproj_generated//generator/xcodeproj:xcodeproj

Bazel Build 的 Target,可以認(rèn)為是我們使用 rules_xcodeproj 定義的生成工程的 Bazel Target。

bc //Article:NewsInHouse applebin_ios-*

在上述 Target 里 rules_xcodeproj 添加了一些 OutputGroupInfo。Bazel Build 時(shí)可以通過 --output_groups 參數(shù)指定輸出產(chǎn)物。

這一條 output group 對(duì)應(yīng)的是 //Article:NewsInHouse 及其依賴 Target 的產(chǎn)物中, swiftmodule 之類的編譯依賴部分。

bg //Article:NewsInHouse applebin_ios-*

這一條 output group 對(duì)應(yīng)的是 //Article:NewsInHouse 及其依賴 Target 的輸入文件中的非源文件的部分,比如編譯時(shí)依賴的 hmap 。

這樣的 Bazel Build 命令可以在 Index Build 時(shí)將 hmap 和 swiftmodule 之類的索引中間產(chǎn)物生成出來,然后再索引具體文件時(shí)就不會(huì)因?yàn)槿鄙龠@些中間產(chǎn)物而失敗。

并且這里的依賴關(guān)系是由 Bazel 去處理的,不是必須像 Xcode 原生機(jī)制那樣,按照依賴關(guān)系來決定 Index Build 中 Target 的順序。

所以僅從 "Module 中間產(chǎn)物" 這方面來說,Xcode 中的依賴關(guān)系并不是必需的。

多 Target 編譯參數(shù)

除了中間產(chǎn)物生成之外,依賴關(guān)系在多個(gè) Target 共用源文件時(shí)的編譯參數(shù)計(jì)算也發(fā)揮著作用。這里的“編譯參數(shù)計(jì)算”是指當(dāng)一個(gè)源文件被不同 Target 引用時(shí),應(yīng)用的編譯參數(shù)可能不同的情況。這么介紹比較抽象,來看下具體的例子:

下面 Demo 工程中有兩個(gè) App Target:AppA 和 AppB

  • 在兩個(gè) App Target 的 Build Settings 中分別注入了宏IS_APP_AIS_APP_B。

圖片圖片

  • 有一份公共的代碼文件 public.m 分別被添加到兩個(gè) App Target 的 Compile Sources 中。

圖片圖片

  • public.m 內(nèi)用預(yù)編譯宏隔離了存在差異的邏輯
  • 隨著我們切換構(gòu)建的 App Scheme,由于編譯參數(shù)的差異,宏作用域中高亮的代碼區(qū)域也會(huì)隨之變化(如下圖)。

圖片圖片

此時(shí)的工程結(jié)構(gòu)如下圖所示,Xcode 可以通過選中的 AppA Scheme 獲取到 AppA Target 的 Build Settings(圖中紅線路徑),正確地傳遞編譯參數(shù)-DIS_APP_A=1

圖片

實(shí)際的情況會(huì)復(fù)雜一些,因?yàn)楣こ痰慕M件化建設(shè)將代碼下沉到了一個(gè)個(gè)組件內(nèi),而非直接與 App Target 關(guān)聯(lián)。此時(shí)同一份代碼文件在不同 App Target 內(nèi)的索引參數(shù)計(jì)算,則是通過 Target 之間的依賴關(guān)系實(shí)現(xiàn)的。

對(duì)應(yīng)到 Xcode 中,Xcode 可以通過 App Target -> Library Target 的依賴關(guān)系,應(yīng)用對(duì)應(yīng)的 Build Settings 生成索引。

圖片

此時(shí)的工程結(jié)構(gòu)則變成了下圖的模式,Xcode 依然可以通過 AppA Target 與 PublicLibraryA Target 的依賴關(guān)系應(yīng)用正確的編譯參數(shù)(圖中紅線路徑)。

圖片

Xcode 原生工程和 Tulsi 生成的 Xcode 工程都是通過這種依賴關(guān)系來保證編譯參數(shù)的正確計(jì)算的。

而 rules_xcodeproj 生成的工程完全移除了 Target 之間的依賴關(guān)系,轉(zhuǎn)而給所有 Target 都添加了對(duì) BazelDependencies 的依賴(如下圖所示)。

圖片

從圖中可以看到,在缺少 AppA Target 對(duì) PublicLibraryA Target 依賴的情況下,對(duì)于同時(shí)被 PublicLibraryA 和 PublicLibraryB 引用的 public.m ,Xcode 無法感知應(yīng)該應(yīng)用哪個(gè) Target 中的編譯參數(shù)(圖中紅線路徑無法關(guān)聯(lián) AppA Scheme 與 public.m)。此時(shí) Xcode 觸發(fā)索引時(shí)應(yīng)用的 Build Settings 是固定的,不會(huì)隨著構(gòu)建 App Scheme 切換而更改。

具體的表現(xiàn)當(dāng)構(gòu)建目標(biāo)從 AppB 切換到 AppA 時(shí),IS_APP_B宏中的代碼仍然會(huì)展示為高亮,而不會(huì)隨之切換,從而給開發(fā)者帶來困惑。

圖片

對(duì)于這個(gè)問題,rules_xcodeproj 可以通過構(gòu)建索引解決,因?yàn)橐蕾囆畔⒃?Bazel 側(cè)(BUILD 文件中)是完整的,所以觸發(fā)構(gòu)建后可以讓代碼高亮正確展示。

但由于編輯索引使用的參數(shù)是 Xcode 從文件所屬的 Library Target 的 Build Settings 中獲取的,因此在代碼編輯過程中仍然會(huì)出現(xiàn)高亮錯(cuò)誤的問題。

構(gòu)建索引:指在構(gòu)建過程中輸出索引產(chǎn)物,需要通過 index-import 導(dǎo)入 Xcode 緩存目錄(Derived Data)下供 Xcode 消費(fèi)。

編輯索引:在代碼編輯過程中實(shí)時(shí)生成的索引,在內(nèi)存中消費(fèi)索引結(jié)果,不會(huì)將產(chǎn)物寫入磁盤。

在這個(gè)場(chǎng)景下,rules_xcodeproj 移除依賴的做法是有缺陷的。

那么我們要在 rules_xcodeproj 恢復(fù) Target 間的依賴關(guān)系么?

答案是不需要。首先,前文有提及大量復(fù)雜的依賴關(guān)系會(huì)導(dǎo)致 Xcode 卡頓,不應(yīng)恢復(fù);其次,要解決這類代碼高亮錯(cuò)誤的問題,需要的其實(shí)并不是所有 Target 之間的依賴關(guān)系,而是源碼文件當(dāng)前構(gòu)建 App Target 的關(guān)系。

回顧一下 Demo 工程最簡(jiǎn)單的結(jié)構(gòu),當(dāng)源碼文件直接被對(duì)應(yīng) App Target 引用時(shí),是不需要 Library Target 間的依賴關(guān)系來建立聯(lián)系的。

圖片

基于這一思路,我們將所有 Library Target 的源碼合并到了對(duì)應(yīng) App Target。當(dāng)然,直接合并源碼以后索引并不能正常工作,需要對(duì)受影響的功能點(diǎn)進(jìn)行適配,這些適配將在下一節(jié)源碼合并方案中展開介紹。

源碼合并方案

索引參數(shù)接管

將所有源碼合并至 App Target 雖然能解決文件與 App 的關(guān)聯(lián)問題,但各個(gè) Library Target 編譯參數(shù)是不同的,聚合之后不同 Target 下源文件的參數(shù)就無法通過 Build Settings 區(qū)分了。

對(duì)于這個(gè)問題,我們是通過 XCBBuildServiceProxy 接管索引參數(shù)計(jì)算解決的。

索引構(gòu)建時(shí),Xcode 會(huì)先將文件所屬 Target 的 Build Settings 發(fā)送給 XCBBuildService 處理成編譯器理解的參數(shù),再交給 SKAgent 觸發(fā)編譯器進(jìn)行實(shí)際編譯行為。而我們?cè)?XCBBuildServiceProxy 的基礎(chǔ)上實(shí)現(xiàn)了 BitSkyXCBBuildService,可以攔截 Xcode 發(fā)給 XCBBuildService 的請(qǐng)求,通過 Bazel aquery 查詢到具體文件的編譯參數(shù),直接返回給 Xcode 完成后續(xù)的索引構(gòu)建行為。

圖片

完成源碼合并索引參數(shù)接管這兩步改造以后,工程結(jié)構(gòu)如下圖所示??梢钥吹?AppA Scheme 能夠直接通過 AppA Target 關(guān)聯(lián)到  public.m(圖中紅線),從而正確地應(yīng)用編譯參數(shù),高亮對(duì)應(yīng)代碼塊。rules_xcodeproj 移除依賴關(guān)系的副作用也完全被修復(fù)。

圖片

Library Target 移除

完成源碼合并以及索引參數(shù)的接管之后,Library Target 中的主要信息( Build Settings 和源碼)都不再有意義了,是否能將這些 Target 信息直接移除呢?

經(jīng)過梳理,Library Target 主要有以下三個(gè)作用,在完成源碼合并以及索引參數(shù)接管后,僅需對(duì)“Module 中間產(chǎn)物生成”進(jìn)行一些改造即可將幾百個(gè) Library Target 的信息進(jìn)行移除,大幅精簡(jiǎn)工程文件的內(nèi)容。

Library Target 作用

說明

適配方案

觸發(fā) Xcode 索引

Xcode 只會(huì)對(duì)添加到 Build Phase - Compile Sources 中的源碼文件生成索引

將源碼添加到 App Target 的 Compile Sources 中有相同的效果;

按 Target 維度隔離 Build Settings

Xcode 原生的索引功能會(huì)通過 Build Settings 生成文件的編譯參數(shù)

通過 XCBBuildServiceProxy 接管索引參數(shù)請(qǐng)求,交由 Bazel aquery 查詢具體文件的編譯參數(shù)

Module 中間產(chǎn)物生成

在 Library Target 的 Build Phase 觸發(fā)各個(gè) Target 維度的中間產(chǎn)物生成

將所需產(chǎn)物聚合到各個(gè) App Target 的 Build Phase 觸發(fā)生成

最終的工程結(jié)構(gòu)如下圖所示。

圖片

整體方案上線后,頭條工程文件(project.pbxproj)行數(shù)從 45w 減少至 35w,工程啟動(dòng)與文件操作耗時(shí)也比原來的 Tulsi 工程減少了 60% 以上。


Tulsi

rules_xcodeproj(原生)

rules_xcodeproj(源碼合并)

工程首次冷啟

47s

22s

16s

二次啟動(dòng)

33s

16s

12s

文件操作

新增 20s

刪除 23s

新增 13s

刪除 11s

新增 8s

刪除 6s

p.s. 源碼合并后存在一個(gè)副作用是 Xcode Build Phase 頁面加載時(shí)間會(huì)增加很多,但考慮到使用 bazel 構(gòu)建后我們并不需要在 Build Phase 修改配置,這個(gè)副作用是可以接受的。

構(gòu)建

rules_xcodeproj 目前提供了兩種 Build 模式,分別是 "Build with Xcode" 和 "Build with Bazel"。

  • "Build with Xcode" 模式下,構(gòu)建行為是由 Xcode 接管的。
  • "Build with Bazel" 模式下,構(gòu)建行為是由 Bazel 接管的。

據(jù) rules_xcodeproj 官方介紹,"Build with Xcode" 模式在 Bazel 7 下將很難支持,并且即將到來的新的增量生成模式也會(huì)放棄 "Build with Xcode" 。

所以這里主要看一下 "Build with Bazel" ,這個(gè)模式生成的工程中,宿主 Target 對(duì)應(yīng)一個(gè) XCScheme,這個(gè) Scheme 的 Build Pre-actions 里生成一個(gè) SCHEME_Target_IDS_FILE 用于記錄 Target 的 Bazel Label。然后宿主 Target 依賴了 Target BazelDependencies,Xcode 在構(gòu)建宿主 Target 之前會(huì)先構(gòu)建 BazelDependencies。BazelDependencies 通過 Build Phase 去調(diào)用 Bazel Build,這個(gè)時(shí)候會(huì)解析 SCHEME_Target_IDS_FILE 獲取需要構(gòu)建的 Bazel Target。BazelDependencies 構(gòu)建完成后,宿主 Target 的 Build Phase 里會(huì)去把相應(yīng)的 Bazel 的產(chǎn)物拷貝到 Xcode Derivedata 目錄下。

圖片

另外,在 rules_xcodeproj 的規(guī)劃中,未來還會(huì)提供一種新的模式,叫做 "Build with Proxy",在這個(gè)模式下,會(huì)通過 XCBBuildServiceProxy 完全繞過 Xcode build system,由 Bazel 控制整個(gè) build 過程。相比 "Build with Bazel" ,這個(gè)模式可以帶來一些更貼近原生的 Xcode 使用體驗(yàn),比如:

  • 無需添加 BazelDependencies Target
  • 可以去掉重復(fù)的 warnings/errors
  • 可以有更穩(wěn)定的索引效果
  • 可以在進(jìn)度條展示更多信息
  • 可以有更詳細(xì)的 Build 報(bào)告

當(dāng)然這種模式也存在比較大的問題

  • 在不同 Xcode 版本之間,Xcode 和 XCBBuildService 交互的 API 可能會(huì)有一些破壞性的變更,需要逐一適配
  • 需要在 Xcode 啟動(dòng)時(shí)注入環(huán)境變量,將 XCBBuildService 指向自定義的 XCBBuildServiceProxy

BitSky 目前采用的方案和 "Build with Proxy" 是類似的,通過 BitSkyXCBBuildService 接管 Xcode 的 build 行為。在用戶點(diǎn)擊 Build 時(shí),BitSkyXCBBuildService 里可以從宿主 Target 的 Build Settings 里解析獲取對(duì)應(yīng)的 Bazel Target,然后再由 BitSky 生成調(diào)用 Bazel Build 的命令,這樣可以保證 Bazel Build 的參數(shù)完全由 BitSky 控制,同時(shí)可以通過 Bazel 的 Build Event Protocol 來更好的提供 Xcode 的進(jìn)度 和 Build 日志展示。

圖片

同時(shí)為了保證在打開生成的 Xcode 工程時(shí),都能夠使用 BitSkyXCBBuildService,BitSky 在生成工程同時(shí),會(huì)生成一個(gè) Xcode 的影子分身 BitSkyXcode。使用這個(gè) BitSkyXcode 打開工程,無需手動(dòng)注入環(huán)境變量,體驗(yàn)上和使用原生 Xcode 打開工程基本一致。

總結(jié)

本文主要介紹了我們將 Xcode 工程生成工具切換到 rules_xcodeproj 過程中做的一些適配和優(yōu)化工作:

  • 索引方面:
  • 在分析 Tulsi 與 rules_xcodeproj 工程文件的過程中我們注意到最大的差異在于 rules_xcodeproj 移除了 Library Target 間的依賴關(guān)系,這也是 Tulsi 工程更加卡頓的罪魁禍?zhǔn)住?/li>
  • rules_xcodeproj 移除依賴關(guān)系后會(huì)導(dǎo)致多 Target 共用的源文件語法高亮異常,我們通過源碼合并方案解決了這個(gè)問題,并且精簡(jiǎn)了工程文件信息,提升了 Xcode 流暢度。
  • 構(gòu)建方面:
  • 我們通過 BitSkyXCBBuildService 接管了 Xcode 的 build 行為,能夠更好地管理構(gòu)建參數(shù)并在 Xcode 提供構(gòu)建進(jìn)度和日志的展示。

在完成切換之后,雖然 Xcode 代碼編輯過程中的卡頓得到了明顯的緩解,但本地研發(fā)的調(diào)試過程,仍然存在 Xcode 卡頓/卡死等現(xiàn)象,對(duì)研發(fā)同學(xué)的開發(fā)工作存在較大困擾。后續(xù),我們將針對(duì)調(diào)試體驗(yàn),從生成工程的角度做一些優(yōu)化工作。

目前考慮基于 Focus Mode 的理念,從底層能力上支持研發(fā)同學(xué)僅關(guān)注與當(dāng)前需求開發(fā)相關(guān)聯(lián)的部分代碼,比如:

  • 裁剪 Xcode 工程中需要索引的源代碼;
  • 裁剪構(gòu)建過程中需要執(zhí)行編譯源代碼;
  • 裁剪調(diào)試時(shí)調(diào)試器需要加載調(diào)試信息;

另外在用戶側(cè),通過策略智能幫助研發(fā)同學(xué),選擇和添加需要 "Focus" 的源碼。

參考文檔

Tulsi (https://github.com/bazelbuild/tulsi)

rules_xcodeproj (https://github.com/MobileNativeFoundation/rules_xcodeproj)

Migrating from Xcode to Bazel (https://bazel.build/migrate/xcode)

Monorepo 解決方案 — Bazel 在頭條 iOS 的實(shí)踐

嗶哩嗶哩 iOS Bazel 進(jìn)化之路

用VSCode基于Bazel打造Apple生態(tài)開發(fā)環(huán)境

責(zé)任編輯:龐桂玉 來源: 字節(jié)跳動(dòng)技術(shù)團(tuán)隊(duì)
相關(guān)推薦

2013-09-16 11:46:40

CDN前端性能Radware

2024-12-11 08:47:47

2009-07-31 11:41:12

光纖連接數(shù)據(jù)中心

2020-03-29 22:51:17

AWS IoT物聯(lián)網(wǎng)IOT

2009-09-24 15:45:23

2020-03-23 14:35:28

前端架構(gòu)應(yīng)用程序

2023-06-21 11:27:53

2009-08-26 18:17:26

C#基于Object解

2016-01-26 18:01:21

2011-04-29 14:53:56

SimpleFrame

2009-04-02 08:23:24

ARM上網(wǎng)本移動(dòng)OS

2012-04-26 14:08:52

2020-12-18 17:27:20

物聯(lián)網(wǎng)資產(chǎn)跟蹤IOT

2013-04-18 09:55:56

iOS開發(fā)Xcode調(diào)試斷點(diǎn)

2014-02-26 09:18:47

應(yīng)用交付優(yōu)化服務(wù)水平協(xié)議

2009-04-03 11:26:12

AMD上海皓龍

2023-04-28 07:37:37

混合云管理云原生

2009-12-11 15:41:18

華為路由器接入

2019-01-18 05:22:39

區(qū)塊鏈智能合約網(wǎng)絡(luò)安全

2024-07-12 11:35:20

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)