背景
移動(dòng)端開發(fā)現(xiàn)狀
在組件化的浪潮下唇撬,公司引入多倉開發(fā)對(duì)工程架構(gòu)進(jìn)行解耦墩弯、跨業(yè)務(wù)技術(shù)能力復(fù)用吩跋,并輔以組件(混合)二進(jìn)制化進(jìn)行編譯提速。不過隨著工程規(guī)模增長渔工、業(yè)務(wù)復(fù)雜度提升锌钮,多倉二進(jìn)制的弊端日益凸顯:
- 合碼效率低下:多倉的引入使開發(fā)流程變復(fù)雜,最有代表性的合碼環(huán)節(jié)一次合碼涉及到主倉和多個(gè)組件引矩,每個(gè)組件要跑 Pipeline 流程進(jìn)行版本發(fā)布梁丘。這種模式提升了 CI 復(fù)雜度,降低了合碼效率旺韭。
- 依賴管理衍生問題:穩(wěn)定性差氛谜,多倉使環(huán)境依賴度變高,穩(wěn)定性變成多個(gè)倉庫穩(wěn)定性的乘積区端。需要手動(dòng)識(shí)別依賴層級(jí)值漫,先發(fā)版被依賴的庫,多版本并行開發(fā)時(shí)织盼,經(jīng)常發(fā)版失敗需要寫死依賴版本杨何。
- 代碼的可視性和可控性降低:跨組件重構(gòu)困難酱塔,全量靜態(tài)檢測無從入手,并且很難統(tǒng)一架構(gòu)規(guī)范危虱;本地開發(fā)體驗(yàn)差羊娃,工程代碼可信度低,無法直接對(duì)代碼進(jìn)行開發(fā)調(diào)試埃跷,本地開發(fā)需要更多的工具和流程來保證代碼的可視性和可控性蕊玷。
誠然,多倉方案很好的落實(shí)了組件化思想捌蚊,但為解決上述多倉體系的問題,公司衍生了大量的優(yōu)化和效率工具近弟,其從結(jié)果看僅聚焦于解決局部問題缅糟,難以進(jìn)一步提升優(yōu)化空間。
目前頭條祷愉、抖音窗宦、B站、谷歌等公司采用了Monorepo的方案解決上述問題二鳄。
● 代碼復(fù)用:Monorepo 可以讓不同的項(xiàng)目或模塊共享代碼和依賴項(xiàng)赴涵,避免重復(fù)編寫和維護(hù)相同的代碼。
● 依賴管理:Monorepo 可以讓不同的項(xiàng)目或模塊共享依賴項(xiàng)订讼,避免依賴沖突和版本管理問題髓窜。
● 統(tǒng)一構(gòu)建:使用 Bazel 可以統(tǒng)一管理 Monorepo 中的構(gòu)建規(guī)則和依賴項(xiàng),提高構(gòu)建效率和可靠性欺殿。
● 版本控制:Monorepo 可以讓不同的項(xiàng)目或模塊共享版本控制系統(tǒng)寄纵,簡化版本管理和發(fā)布流程。
Monorepo 全源碼概念和優(yōu)缺點(diǎn)
Monorepo(Mono Repository)指的是將多個(gè)相關(guān)項(xiàng)目或模塊的代碼集中管理于一個(gè)倉庫的軟件開發(fā)模式脖苏。具體來說程拭,Monorepo 將所有的源代碼和配置文件等存儲(chǔ)在一個(gè)版本控制倉庫中,并使用相同的構(gòu)建和部署系統(tǒng)來管理和交付代碼棍潘。
全源碼(Full Source)是指將整個(gè)軟件系統(tǒng)的源代碼均存放于一個(gè)倉庫中恃鞋,源碼為單一可信源。
全源碼的概念通常與 Monorepo 聯(lián)系在一起亦歉,將概念定義為 Monorepo 全源碼解決方案恤浪,是區(qū)分于當(dāng)前流行的組件二進(jìn)制化開發(fā)模式。下圖中 MULTI-REPO 為前多倉開發(fā)模式肴楷,每個(gè)組件獨(dú)立 git 存儲(chǔ)资锰;MONO-REPO 為單倉模式,組件同主倉合并阶祭,只保留一個(gè) git绷杜。
優(yōu)點(diǎn):
● 便于代碼復(fù)用:所有項(xiàng)目代碼集中于單一倉庫直秆,相似的功能更便捷地抽象為公共庫,并直接由項(xiàng)目引用鞭盟。提高代碼的可維護(hù)性和開發(fā)效率圾结。
● 簡化依賴管理:無需依賴管理器,所有引用的依賴項(xiàng)都存在于同一代碼庫中齿诉,更加方便地進(jìn)行構(gòu)建和管理筝野。
● 原子提交:可以原子性地更改多個(gè)項(xiàng)目以避免多倉下不同版本依賴同步的兼容性問題。
● 大規(guī)模代碼重構(gòu):開發(fā)人員可訪問整個(gè)項(xiàng)目粤剧,從而更加容易地進(jìn)行代碼重構(gòu)歇竟,并確保每個(gè)部分都能正常工作。
● 跨團(tuán)隊(duì)協(xié)作:通過源代碼依賴改進(jìn)其他團(tuán)隊(duì)正在開發(fā)的項(xiàng)目抵恋,從而實(shí)現(xiàn)代碼所有權(quán)的靈活性焕议。
● 工程質(zhì)量提升:Monorepo 倡導(dǎo)開放,透明弧关,共享的組織文化盅安,有利于開發(fā)者成長,代碼質(zhì)量的提升世囊。
缺點(diǎn)
● 版本信息丟失:Monorepo 使用相同的版本號(hào)别瞭,無法對(duì)每個(gè)項(xiàng)目進(jìn)行單獨(dú)的版本控制。(多端同倉)
● 缺乏權(quán)限控制:無法根據(jù)需要授予對(duì)代碼庫的訪問權(quán)限株憾,所有代碼都在同一個(gè)項(xiàng)目中可能存在安全問題蝙寨。
● 磁盤空間占用:默認(rèn)情況下需檢出所有項(xiàng)目,需要更多的存儲(chǔ)空間嗤瞎。
為何選擇Bazel
Bazel 是一種快速籽慢、正確且可擴(kuò)展的構(gòu)建工具,具有集成測試功能猫胁,可在行業(yè)領(lǐng)先的生態(tài)系統(tǒng)中支持多種語言箱亿、代碼庫和平臺(tái)。
Bazel 速度快
Bazel 可以確切知道每個(gè)構(gòu)建命令需要哪些輸入文件弃秆,從而僅在一組輸入文件在每次構(gòu)建之間發(fā)生更改時(shí)重新運(yùn)行届惋,從而避免不必要的工作。 它會(huì)在同一計(jì)算機(jī)或遠(yuǎn)程構(gòu)建節(jié)點(diǎn)上以盡可能高的并行性運(yùn)行構(gòu)建命令菠赚。如果構(gòu)建結(jié)構(gòu)允許脑豹,它可以同時(shí)運(yùn)行數(shù)千個(gè)構(gòu)建或測試命令。
這受到內(nèi)存衡查、磁盤和遠(yuǎn)程構(gòu)建農(nóng)場(如果有)的多個(gè)緩存層支持瘩欺。在 Google,我們通常會(huì)將緩存命中率達(dá)到 99% 以上。
Bazel 正確
Bazel 可確保您的二進(jìn)制文件僅基于您自己的源代碼構(gòu)建俱饿。Bazel 操作在單獨(dú)的沙盒中運(yùn)行歌粥,Bazel 會(huì)跟蹤構(gòu)建的每個(gè)輸入文件,并且只會(huì)在需要時(shí)重新運(yùn)行構(gòu)建命令拍埠。這樣可以使您的二進(jìn)制文件保持最新狀態(tài)失驶,以便相同的源代碼始終生成相同的二進(jìn)制文件。
對(duì)無休止的 make clean
調(diào)用枣购,以及追尋實(shí)際上在從未被構(gòu)建的源代碼中解決的幽靈 bug 的說辭嬉探。
Bazel 具有可擴(kuò)展性
自行編寫規(guī)則和宏,針對(duì)各種項(xiàng)目的特定需求自定義 Bazel棉圈,從而充分發(fā)揮 Bazel 的功能涩堤。
Bazel 規(guī)則是使用 Starlark 編寫的,Starlark 是我們的內(nèi)部編程語言分瘾,是 Python 的子集胎围。Starlark 讓大多數(shù)開發(fā)者能夠使用規(guī)則編寫功能,同時(shí)還能創(chuàng)建可在整個(gè)生態(tài)系統(tǒng)中使用的規(guī)則芹敌。
遷移構(gòu)建系統(tǒng)Bazel
- 遷移至 Bazel 體系痊远,配置BUILD和WORKSPACE
- 兼容同時(shí)使用cocoaPods和Bazel
- .podspec轉(zhuǎn)BUILD 開源方案
- install.sh 腳本改造
最終目錄如下:
cocoapods生成的工程叫Test1Bazel.xcworkspace垮抗,Bazel生成的工程叫:xxxxxBazel.xcodeproj
Build文件:
load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application")
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "lib",
srcs = glob(["Sources/Delegate/*.swift"]),
data = [
"Resources/Main.storyboard",
],
deps = ["http://Sources/Network:AFNetworking"],
)
ios_application(
name = "xxxxxxBazel",
bundle_id = "com.wuwei.swift",
app_icons = glob(["Assets.xcassets/AppIcon.appiconset/**"]),
families = [
"iphone",
],
infoplists = ["Resources/Info.plist"],
launch_storyboard = "LaunchScreen.storyboard",
minimum_os_version = "14.0",
visibility = ["http://visibility:public"],
deps = [":lib"],
)
load(
"@rules_xcodeproj//xcodeproj:defs.bzl",
"top_level_target",
"xcodeproj",
)
xcodeproj(
name = "xcodeproj",
build_mode = "bazel",
project_name = "xxxxxxBazel",
tags = ["manual"],
top_level_targets = [
":xxxxxxBazel",
],
)
管理工程依賴
CocoaPods 在多倉模式下除去自身的依賴管理能力外氏捞,承擔(dān)了非常多的非自身功能,
如組件二進(jìn)制集成冒版、hmap 的編譯優(yōu)化液茎,混編能力的支持。
遷移 Bazel 構(gòu)建系統(tǒng)后這些優(yōu)化能力名正言順的通過構(gòu)建系統(tǒng)來承擔(dān)辞嗡。
在 Monorepo 生態(tài)中我們也應(yīng)該設(shè)立相應(yīng)的組件依賴關(guān)系驗(yàn)證及組件模塊分層服務(wù)捆等。Bazel 可以設(shè)置所有 target 的可見域,開發(fā)者可以將 target 標(biāo)記為私有续室,以防止被其他項(xiàng)目錯(cuò)誤地依賴栋烤,這種約束幫助我們規(guī)范代碼倉庫之中各個(gè)庫之間的依賴關(guān)系。
優(yōu)化本地體驗(yàn)
在本地開發(fā)中通過開源工具 Tulsi挺狰、BuildService明郭、IndexImport 的定制化開發(fā),補(bǔ)齊 Xcode 體系中索引丰泊、高亮薯定、日志、進(jìn)度條等語言能力瞳购,使 Xcode 體驗(yàn)與之前無異话侄。通過這些工具我們把 Xcode 的優(yōu)化牢牢把握在自己的手上,也很好的提升了 Xcode 易用性。
Tulsi已經(jīng)不維護(hù)了年堆,官方推薦rules_xcodeproj
最后
Bazel生成的Xcode工程里面的Build settings配置項(xiàng)需要額外的腳本快速配置吞杭,因?yàn)锽uild文件改動(dòng)后Xcode工程每次需要重新生成,手動(dòng)修改不現(xiàn)實(shí)嘀韧。
最后的最后篇亭,我想說:我最大的愿望是世界和平!锄贷!