天天看點

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

作者:閃念基因

一、頭條的困境與出路

頭條工程的困境

近年來,頭條工程經曆了以下幾個階段:

  • 2016 年開始使用 CocoaPods 管理二方元件,并逐漸從 Monolith 遷移到 Multi-repo。
  • 2017 年實作了元件依賴打平群組件二進制化,通過共享預建構的二進制檔案,縮短了建構時間。
  • 2021 年初,通過 CC 宏接入 Dolphin 的 clang wrapper,将 CI 建構時間減少了 50%。
Dolphin 是位元組内部分布式緩存系統。Dolphin通過最小化的降低真正需要編譯的源檔案的數量,來提升整個的編譯效率。當編譯一個源檔案的時候,Dolphin 會通過一系列的計算,判斷這個源檔案是否需要真正的調用編譯器進行複雜的全流程編譯,如果發現滿足若幹條件,Dolphin 會直接從自己的緩存系統的擷取編譯産物,這個時長比編譯器本身編譯要短很多。

然而,這些方案也帶來了一些明顯的副作用:

  • 統一建構問題:元件二進制化要求元件高度标準化,無法注入客制化邏輯,如預編譯宏等手段均會失效。
  • 建構穩定問題:內建元件階段強依賴網絡環境,下載下傳成功率無法保證 100%,首次建構需要同時下載下傳 500+ 的元件,時間占比高達 30%。
  • 緩存複用問題:使用 CC 宏接入 clang wrapper 則天然缺失部分上下文資訊,導緻編譯緩存容易被污染,緩存複用率低,并且作用範圍有限,不能覆寫到 actool/ibtool 等工具的産物。

頭條工程的出路

考慮到頭條工程所面臨的問題,于 2021 年中開始探讨采用 Monorepo + Bazel 的解決方案。在《iOS Monorepo 全源碼解決方案》一文中,提到了使用 Bazel 建構 Monorepo 工程的優勢,包括:

  • 代碼複用:Monorepo 可以讓不同的項目或子產品共享代碼和依賴項,避免重複編寫和維護相同的代碼。
  • 依賴管理:Monorepo 可以讓不同的項目或子產品共享依賴項,避免依賴沖突和版本管理問題。
  • 統一建構:使用 Bazel 可以統一管理 Monorepo 中的建構規則和依賴項,提高建構效率和可靠性。
  • 版本控制:Monorepo 可以讓不同的項目或子產品共享版本控制系統,簡化版本管理和釋出流程。

具體來說,頭條工程的遷移至 Bazel 可分為兩個階段:

第一階段:遷移業務元件。 基于 JoJo 完成了業務元件源碼化(約 10,000 檔案數)及 Bazel(3.x)化改造,初步解決了建構耗時問題,取得了不錯的效能提升。

第二階段:遷移二三方元件 。 在依賴管理和統一建構上做更激進的嘗試,基于新版本的 Bazel(5.x)和 Rules 對二三方元件進行源碼化(約 18,000 檔案數)和 Bazel 化改造,并完全移除依賴管理工具 CocoaPods,統一建構,提升工程穩定性,BitSky 項目基于該階段提出。

本文接下來的内容,将重點介紹第二階段過程中,頭條是如何基于 BitSky 結合 Bazel 進行工程改造的。

BitSky 建構服務

在 BitSky 中,Bazel 作為建構系統的核心,負責管理和建構應用程式和庫檔案的編譯、連結、測試等過程,并支援自定義建構規則和工具鍊的內建。為了更好地了解 BitSky 的建構系統,我們先介紹 Bazel 的基礎概念,再介紹 BitSky 的建構系統分層。

Bazel 概念簡介

Bazel 是 Google 内部使用(Blaze)并開源的一個通用建構系統,并且内置支援建構用戶端和服務端軟體,包括 Android 和 iOS 平台的用戶端應用程式;還提供了一個可擴充的架構(Rules),可以使用它來開發自己的建構規則。Hermeticity(封閉性)是 Bazel 建構系統的一個重要概念,指的是建構過程的可重制性和可靠性。在 Hermeticity 的理念下,Bazel 會盡可能地隔離建構過程中的環境和依賴項,進而確定建構結果的一緻性和可重複性。

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

以下是一些 Bazel 常用基礎概念,了解其中一些概念有助于了解本文後續的内容:

Bazel 常用基礎概念:https://bazel.build/concepts/build-ref
  • BUILD files(建構檔案):用于描述一個 Bazel 項目的建構規則和依賴關系,類似于 CocoaPods 中的.podspec檔案。
    • 類似于.podspec檔案中的源檔案和資源檔案,我們可以在BUILD檔案中聲明需要編譯的源檔案、資源檔案和其他依賴庫等資訊。
    • 和.podspec檔案類似,BUILD檔案也支援類似于版本控制的文法,可以指定具體的版本、分支或者送出号等資訊。
  • Workspace(工作空間):每個工作空間都包含一個WORKSPACE檔案,用于管理一個 Bazel 項目的依賴關系,類似于 CocoaPods 中的Podfile檔案。
    • 類似于Podfile中的 pod,我們可以在WORKSPACE檔案中聲明依賴關系,指定需要依賴的庫、二進制檔案或者其他項目。
    • 還類似于Podfile.lock,WORKSPACE檔案不會鎖定依賴的版本,而是在每次建構時重新解析依賴關系,以確定建構的一緻性和可重複性。
    • WORKSPACE檔案還可以定義一些全局配置,比如編譯器選項、建構工具選項等等。這些配置可以被整個項目共享,確定項目的建構和依賴關系的一緻性和可重複性。
  • Packages(包):是包含一個BUILD檔案的目錄,用于組織和管理代碼庫中的源代碼和建構規則。
  • Targets(目标):表示建構規則和依賴項的目标,它可以是源檔案、庫檔案、可執行檔案等等。
  • Labels(标簽):辨別建構規則和依賴項,它由包名和目标名組成。

BitSky 建構系統

本章節介紹為 BitSky 中的建構系統子產品,完整的 BitSky 子產品架構請關注之前文章:《iOS Monorepo 全源碼解決方案》。BitSky 的建構系統分為五個分層,包括工程配置、軟體服務、建構系統、建構規則和建構工具,如下圖所示:

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

工程配置

為宿主工程提供客制化的配置能力,以及語義化的調用指令。這一層的作用是讓使用者可以友善地配置 BitSky 建構系統。

  • Makefile 定義了核心的調用指令,并且都是語義化聲明,友善使用者生成工程以及完成建構。
  • Plugin(插件) 通過提供不同時機的 hook 函數,以及可定向化配置 Rules 的入參,同時支援配置自定義 xctoolchain,可滿足大部分客制化訴求。

軟體服務

包含 BitSky、Tulsi 和 BuildService 三個工具。這一層的作用是為使用者提供一系列建構工具,使得使用者可以更加友善地使用 BitSky 建構系統。

  • BitSky 負責橋接現有的宿主.xcodeproj工程檔案,基于自研的輕量依賴管理能力,自動轉換為 Bazel 建構所需的WORKSPACE/BUILD檔案,不需要終端使用者手工生成工程物料。
  • Tulsi 工具,用于生成能夠使用 Bazel 建構的 Xcode 工程,也通過 BitSky 進行調用。
  • BuildService 工具,用于補齊 Xcode 體系中索引、高亮、日志、進度條等能力。

建構系統

Bazel 建構系統的設計理念是分層架構,其中建構系統層、建構規則層和建構工具層是由 Bazel 決定的。是以建構系統層也是 BitSky 的核心層,包含 Bazel、Dep Server 和 Remote Execution Services,這一層的作用是為了支援分布式計算叢集上執行建構和測試任務。

  • Bazel 建構系統的核心是一個高度優化的建構引擎,用于排程所有建構指令、提供編譯、運作、依賴查詢等能力,并支援分布式計算叢集上執行建構和測試任務。
  • Dep Server(依賴解析服務) 用于在 Bazel 生成 Action 時做依賴矯正。在《Monorepo 解決方案之 Remote Execution》中有相關的介紹。
  • Remote Execution Services(遠端執行服務) 是一種分布式建構和測試系統,用于在雲上或者分布式計算叢集上執行建構和測試任務。BitSky 的 Bazel 建構系統就是在公司内部的叢集上執行建構和測試任務,以提高開發效率和代碼品質。

建構規則

Rules(建構規則)是 Bazel 建構系統的執行基石——定義建構規則和依賴項的基本單元。Rules 是基于 Starlark 語言(Python 子集語言),将建構過程分解為一系列可重複的步驟,并定義了這些步驟之間的依賴關系。這一層的作用是為了保證建構的正确性和可重複性。

  • Embedded Rules( Bazel 内置的 Rules) 包括objc_library、cc_library等常用規則,用于定義和管理編譯、連結、測試等建構規則和依賴項。
  • Apple Rules 包含 Bazel 對蘋果平台(iOS、macOS、watchOS、tvOS)的建構支援規則,如apple_binary、apple_library等,用于定義和管理蘋果平台應用程式和庫檔案的編譯、連結和打包規則。
  • bazel_generator,負責把 .podspec檔案轉換成BUILD檔案。Bazel 建構系統能正常運作取決于調用編譯器、連結器等工具鍊時編譯參數以及建構依賴的正确性,通過該研發工具将建構規則遷移至 Bazel 體系。

建構工具

是 Bazel 建構系統的工具集合,包含 Rules 提供的 Wrapped Tools 和宿主工程提供的 Custom Toolchains 兩個部分。這一層的作用是為了提供必要的建構工具。

  • Wrapped Tools 是通過 Bazel 的工具包裝機制,将官方釋出的編譯器和連結器等工具包裝為可移植的二進制檔案,用于在 Bazel 建構系統中編譯、連結和測試應用程式和庫檔案。
  • Custom Toolchains 是通過配置檔案,将特定版本的編譯器、連結器等工具內建到 Bazel 建構系統中,用于建構和測試應用程式和庫檔案。

頭條遷移至 Bazel

如前文所說,頭條工程在前幾年已經完成了元件化的演進,近 500+ 個元件都由 CocoaPods 進行包管理,每個元件都有一個.podspec檔案,用于描述元件的版本資訊、建構所需的源檔案和配置等。從這個角度來看,BUILD檔案和.podspec檔案的作用類似。在遷移到 Bazel 建構系統時,頭條工程隻需要添加WORKSPACE檔案,并将.podspec檔案轉換為BUILD檔案即可。WORKSPACE檔案主要用于描述外部依賴,接入成本較低,是以不在此讨論,本文重點介紹BUILD檔案的轉換思路。

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

在 Bazel 建構系統中,Objective-C 源檔案可以使用objc_library規則作為最小編譯目标,而 Swift 源檔案則對應swift_library規則。以objc_library為例,bazel_generator 将.podspec檔案轉換為BUILD檔案的過程如下:

  1. 解析.podspec檔案,包括依賴庫、源檔案、編譯選項等。
  2. 生成BUILD檔案。根據.podspec檔案中的資訊,bazel_generator 會生成适用于 Bazel 的BUILD檔案。對于objc_library規則,生成的BUILD檔案通常包括以下内容:
    1. name: 定義庫的名稱。
    2. srcs: 定義源檔案清單。
    3. deps: 定義依賴庫清單。
    4. copts: 定義編譯選項。
  3. 處理依賴關系。由于 Bazel 和 CocoaPods 的依賴管理方式不同,bazel_generator 需要處理依賴關系。具體來說,bazel_generator 會将 CocoaPods 中的依賴庫轉換為 Bazel 中的依賴庫,并将其添加到BUILD檔案中的 deps 清單中。
  4. 處理資源檔案。如果.podspec檔案中包含資源檔案,bazel_generator 會将其轉換為 Bazel 中的 data 屬性,并将其添加到BUILD檔案中。
  5. 處理其他配置項。bazel_generator 還會處理其他一些配置項,例如編譯選項、頭檔案搜尋路徑等。

通過這些步驟,bazel_generator 将.podspec檔案轉換為适用于 Bazel 的BUILD檔案,并自動處理依賴關系、資源檔案等問題,進而簡化了遷移過程。

在了解了.podspec檔案轉換為BUILD檔案的過程後,下面我們介紹一下頭條是如何完成元件遷移的。首先,頭條對 500+ 二進制化元件進行拆分,分為業務元件和二三方元件。

第一階段

業務元件遷移

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

由于業務元件僅內建到頭條工程,并不會提供給其他工程複用,是以頭條優先考慮将業務元件全源碼化并內建到主倉的Module目錄中。這樣做的好處是,可以減少二進制依賴,提高建構效率,同時也友善開發人員進行維護和更新。

  • 對于業務元件的BUILD檔案,頭條會首次自動生成,後續則由研發人員進行維護和更新。
  • 對于二三方元件,則繼續存放在Pods目錄中,并通過 CocoaPods 進行內建。

第二階段

二三方元件遷移

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

為了友善管理和更新二三方元件,頭條将其全源碼化并內建到主倉的External目錄中,并通過monorepo_config.yml和deps.yml這兩個檔案提供版本和依賴管理的憑證。

  • 對于二三方元件的BUILD檔案,頭條使用 bazel_generator 工具,通過.podspec檔案自動轉換生成。這樣做的好處是,可以減少手動編寫BUILD檔案的工作量,提高工作效率。
  • monorepo_config.yml檔案記錄了所有元件對應的倉庫資訊(Git 來源/二進制連結來源)群組件版本資訊,用于元件源代碼回溯。這樣可以友善地查找和管理元件的源代碼,同時也可以保證元件的版本一緻性。
  • deps.yml檔案記錄了工程中各個 Target 的依賴元件及包含的 subspecs。這樣可以清晰地了解工程中各個 Target 的依賴關系,便于管理和維護。

工程配置插件化

經曆完第二階段後,頭條已經完成了元件的 Monorepo 全源碼化,放棄了 CocoaPods 作為依賴管理工具,轉而将所有元件放到宿主工程中。這帶來了以下問題:

  • 元件 建構選項管理: Bazel 是基于産物的建構系統,建構選項隻能在各個元件的BUILD檔案中控制,而 CocoaPods 的 Hook 調用時機在內建階段,統一對元件的建構選項做修改,屬于中心化管理。
  • 元件 配置條件管理: 部分元件的建構選項修改需要特殊處理,不适用統一修改的方案,需要使用黑白名單的方式來控制,并且需要對應元件版本,管理容易出問題,隐蔽且排查困難。

是以,建構系統需要具備管理感覺能力,Bazel 成為更好的選擇。我們不應該在依賴管理工具中介入建構選項,應該将其隔離開來。為了滿足宿主工程的定制化需求,我們需要提供具備以下能力的機制:

  • 宿主可以通過中心化管理的方式定制配置各個元件的建構目标參數,并且可透傳 Bazel 選項,實作更加靈活的建構流程。
  • 開發者可以根據不同的條件選擇性地應用建構規則,進而實作更加靈活和定制化的建構流程。

為了更好地介紹這個機制,我們首先需要了解 BitSky 和 Bazel 的調用時序:

  1. 使用者直接調用 BitSky 的指令。
  2. BitSky 根據場景組裝出相應的 Bazel 建構選項。
  3. BitSky 在 "to bazel opts" 階段通路 Plugin 内容。
  4. Plugin 是可客制化的中間層,用于傳遞宿主工程的 Bazel 建構選項。
  5. BitSky 将組裝好的 Bazel 指令選項傳遞給 Bazel 進行調用。
Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

從上圖可看出,我們在 Plugin(插件)中可以根據不同宿主工程的需求提供定制化的建構選項,進而也降低了 BitSky 和 Bazel 之間的耦合度。為了達到該目的,我們結合 Bazel 的特性,把插件的組成劃分為以下 4 個部分,接下來的内容将會一一介紹各個部分。

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

1. 注冊鈎子函數——hook.py

鈎子函數是工程配置插件化的重要組成部分,其作用是在特定的時機執行額外的配置工作或操作。BitSky Plugin 提供了四個鈎子函數,如下圖所示:

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐
  • pre_generate_material_hook(obj)和post_generate_material_hook(obj)分别在生成宿主工程WORKSPACE檔案和BUILD檔案之前和之後調用,并且通過 obj 提供所需的建構參數。可以在這個時機進行額外的配置工作,如根據不同的建構場景拉取對應的配置檔案等。
  • pre_build_hook(obj)和post_build_hook(obj)則在建構前後調用,可以用于執行額外的操作,如清理、打包、上傳等。

2. 定向配置建構目标參數——defs.bzl

defs.bzl是 Bazel 的一個規則檔案,用于定義自定義的規則和函數。在 BitSky Plugin 中,defs.bzl檔案定義了宿主工程所需的建構選項和自定義規則,并提供了統一管理宿主工程的建構目标入參的功能,解決元件 建構選項管理的問題。如下圖所示,可定向對ios_application依賴的objc_library規則傳入客制化的 copts。

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

結合 Bazel 的特性,defs.bzl能夠解決以下具體問題:

  • 定向配置建構目标參數:Bazel 支援多種不同的建構目标和參數,defs.bzl檔案通過傳值機制,可以幫助開發者針對不同的宿主工程進行定向配置建構目标參數,確定 BitSky 不需要關注各宿主工程的具體目标參數,而是由各宿主工程自行決策。
  • 自定義規則和函數:Bazel 提供了豐富的規則和函數,但有時候需要自定義規則和函數來滿足特定的需求,defs.bzl檔案可以幫助開發者定義自己的規則和函數,實作更加靈活和定制化的配置。
  • 統一管理宿主工程的建構目标入參:Bazel 支援分布式建構,但不同的BUILD檔案可能會有不同的建構目标入參,defs.bzl檔案可以幫助開發者實作統一管理宿主工程的建構目标入參,包括 BitSky 自動生成的和研發維護的BUILD檔案。
  • 增強工程配置的可擴充性:Bazel 的規則和函數非常豐富,但有時候需要自定義規則和函數來滿足特定的需求,defs.bzl檔案可以幫助開發者定義自己的規則和函數,實作更加靈活和定制化的配置,增強工程配置的可擴充性。

3. 透傳 Bazel 選項——bazelrc

在 Bazel 官網的最佳實踐中提及:工程的特定選項可使用.bazelrc檔案管理。通過--bazelrc=<path to rc>傳入指定.bazelrc檔案,用于設定 Bazel 的運作時參數和環境變量。

Bazel-最佳實踐:https://bazel.build/configure/best-practices#bazelrc-file
Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

.bazelrc檔案可以定義諸如建構選項、建構緩存、建構工具鍊、建構輸出路徑等等配置選項。配置使用.bazelrc檔案有以下好處:

  • 定制化建構選項:.bazelrc檔案可以定義建構選項,例如編譯器的版本、編譯參數、建構輸出路徑等等,可以根據不同的需求和場景進行靈活的配置和擴充,滿足不同項目的需求。
  • 管理建構工具鍊:.bazelrc檔案可以定義建構工具鍊,可以根據不同的需求和場景進行配置,友善管理和維護建構工具鍊。
  • 提高建構的可移植性:.bazelrc檔案可以定義建構選項和建構工具鍊,可以根據不同的需求和場景進行配置,提高建構的可移植性,使得建構結果更加穩定和可靠。

綜上,.bazelrc檔案可以幫助開發者更好地管理和維護建構配置,提高配置的靈活性、可維護性、可複用性、可擴充性和可移植性。

4. 限定配置條件——conditions

conditions(條件)是 Bazel 中用于根據不同的條件選擇性地應用建構規則的一種機制。通過 conditions,開發者可以根據不同的條件(如作業系統、編譯器版本、CPU 架構等)選擇性地應用建構規則,進而實作更加靈活和定制化的建構流程,解決元件 配置條件管理的問題。以下是頭條工程中的一個應用場景:

  • Bazel 内置的配置條件是//conditions:default,為統一代碼風格,宿主工程可在conditions目錄下的BUILD檔案中聲明自定義配置條件,便于用以下方式通路自定義的條件标簽://conditions:debug或//conditions:release。
  • 在建構規則中使用select()函數區分 debug / release 配置下所需的選項,通過這種機制對齊 Xcode 中的 Debug / Release 配置;對于有多個配置的工程,可以按需增加config_setting規則。
Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

頭條的收益

頭條工程在各個階段的遷移中,均取得了不錯的效能提升:

  • 第一階段基于 JoJo 完成業務元件遷移後,整體建構耗時 PCT50 降低 38%,AVG 降低 45%。
Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐
  • 第二階段基于 BitSky 完成二三方元件遷移後,Build 耗時 PCT50 降低 20%,PCT 90 降低 50%;整體建構耗時降低 50%。
Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐

總結

問題和挑戰

在頭條工程遷移至 Bazel 過程中,除了上文提及的問題,還遇到了三個主要的挑戰,包括:

  • 二三方元件依賴治理
  • 産物一緻性校驗
  • 建構環境統一

為了解決這些問題,頭條工程采取了一系列的措施,保證遷移的順利進行。

二三方元件依賴治理

由于曆史原因,頭條工程在pod analyze階段會忽略.podspec檔案聲明的元件依賴資訊,完全置信于Podfile檔案維護的元件版本。如果是內建二進制後的二三方元件,這個方案能極大地降低pod analyze階段的耗時。若內建全源碼化的二三方元件,因元件.podspec檔案中聲明的元件依賴資訊和Podfile檔案中聲明的未必一緻,有較大機率導緻建構失敗;同時,使得原本能在pod analyze階段發現的問題,推遲到了建構甚至運作時才能被發現,也使得元件的增删改等維護變得複雜。在遷移的過程中,頭條工程會處于建構系統雙跑階段,為保障 Bazel 建構成功率,也同步進行二三方元件依賴治理:

  • 借用自研工具快速決議元件版本沖突群組件依賴聲明缺失,過濾出問題元件。
  • 聯系元件維護人,确認問題元件在頭條工程中的版本号。
  • 完善問題元件.podspec檔案中的元件依賴資訊。

産物一緻性校驗

二三方元件遷移的過程中,由內建二進制檔案改成內建源碼,而各元件二進制化的建構環境和宿主工程目前的建構環境未必一緻,這就造成二三方元件遷移後産出的二進制檔案和原本的不一緻,最終導緻程式在運作時的表現有差異。為此,在建構系統雙跑階段,會自動觸發校驗工具對比遷移前後的産物,并輸出有差異的符号。

  • 校驗工具的基本原理是解析 Mach-O 中segment_command_64擷取映射到程式位址空間的位置和大小,然後基于.linkmap檔案中的符号資訊,計算出這些符号真正的檔案偏移量位址,然後讀出記憶體資料進行對比。具體實踐會在後續的系列文章中介紹。
  • 根據符号定位到對應的元件,對比遷移前後的建構日志,定位差異的選項。
  • 前期主要用于雙跑階段的産物對比,消費了 1000+ 差異符号,對齊編譯層面的選項;後期用于工具鍊更新的保障措施,確定更新引入的差異符合預期。

建構環境統一

頭條工程的建構環境可以分為兩種:CI-CD 和本地研發。CI-CD 是在雲服務上部署的,建構環境是統一且可控的,叢集内的裝置都部署了相同版本的工具鍊。而本地研發的建構環境則更加多樣化且不受控制。其中最明顯的差異是建構所使用的Xcode版本。在本地研發環境下,頭條工程會生成多個 Xcode 版本的建構緩存,影響本地研發的建構緩存複用。此外,不同的 Xcode 版本包含的工具鍊版本也不同,是以無法保證本地研發和 CI-CD 的建構産物一緻,如下圖所示:

Xcodecctoolsld64LLVMClangSwift14.01001.2819.614.0.014.0.0 (clang-1400.0.29.102)5.7 (swiftlang-5.7.0.127.4 clang-1400.0.29.50)14.0.11001.2819.614.0.014.0.0 (clang-1400.0.29.102)5.7 (swiftlang-5.7.0.127.4 clang-1400.0.29.50)14.11001.2820.114.0.014.0.0 (clang-1400.0.29.202)5.7.1 (swiftlang-5.7.1.135.3 clang-1400.0.29.51)14.21001.2820.114.0.014.0.0 (clang-1400.0.29.202)5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)14.31005.2857.115.0.014.0.3 (clang-1403.0.22.14.1)5.8 (swiftlang-5.8.0.124.1 clang-1403.0.22.11.100)14.3.11005.2857.115.0.014.0.3 (clang-1403.0.22.14.1)5.8.1 (swiftlang-5.8.0.124.5 clang-1403.0.22.11.100)

通過上文提及的限定配置條件,指定支援的 Xcode 版本,可解決部分建構環境不統一的問題:

Monorepo 解決方案 — Bazel 在頭條 iOS 的實踐
  • conditions/BUILD檔案定義xcode_config 規則,标簽是//conditions:host_xcodes,用于聲明支援的 Xcode 版本。
  • .bazelrc檔案設定--xcode_version_config=//conditions:host_xcodes,指定目前工程使用的 Xcode 版本。

頭條的經驗

本文主要介紹了頭條遷移至 Bazel 的曆程,包括建構系統的架構分層和接入方案,以及結合 Bazel 的特性來優化工程配置的管理。頭條遷移至 Bazel 後,研發效率和建構穩定性都有顯著提升。由于采用了 Monorepo 全源碼的內建方案,頭條也加快了依賴治理和架構演進方向的工作進展。然而,由于篇幅限制,本文隻介紹了遷移 Bazel 過程中的部分細節,而 Infra 團隊在這一路的探索遠不止于此。在本系列的文章中,我們将繼續介紹頭條在本地研發IDE和開發流程遷移方面所面臨的挑戰。

作者:App Infra

來源:微信公衆号:位元組跳動終端技術

出處:https://mp.weixin.qq.com/s/ajkOP7yeSYCcE0MoUUmUZA

繼續閱讀