前言
本方案適合于單倉庫(Monorepo)方式管理的項目秩命,通過二進(jìn)制化的想法減少編譯工作量军俊,并通過拋棄Xcode的自帶的依賴管理機制侥加,建立自定義的依賴管理去實現(xiàn)開發(fā)時的編譯加速。本方案可做到對原項目無侵入性粪躬,兼容兩套依賴管理機制担败,既可用Xcode進(jìn)行依賴管理昔穴,也可以使用自定義的方式進(jìn)行依賴管理,任何項目均可放心使用提前。
(本方案基于項目已經(jīng)組件化吗货,未組件化項目可先進(jìn)行組件化再實施,整體改造難點在于依賴管理改造)
項目改進(jìn)后岖研,會有飛一半的質(zhì)感卿操,讓大項目的編譯速度回歸到小項目一樣。
如圖孙援,修改了組件A害淤、組件C情況下,原Xcode依賴管理方案進(jìn)行管理時拓售,執(zhí)行build clean后會重新編譯所有代碼編譯窥摄。依賴管理改進(jìn)方案只需要執(zhí)行編譯主工程、組件A础淤、組件C崭放,即完成編譯工作,大大縮短了編譯時間鸽凶。
對Xcode熟悉以及對自身項目熟悉的開發(fā)者兩天內(nèi)可以完成改造币砂,最長不超過一周時間,可以說成本很低玻侥,而且不影響原有項目的構(gòu)建方式决摧。
思路與原理
-
二進(jìn)制組件化與多倉庫管理陷阱
通過對組件預(yù)先編譯得到二進(jìn)制文件,那么在開發(fā)時主工程只需要執(zhí)行鏈接的過程就可以完成構(gòu)建工作凑兰。
如圖2所示掌桩,通過將主工程拆分為組件后,通過對組件預(yù)先編譯得到二進(jìn)制文件姑食,主工程構(gòu)建時只需要對組件進(jìn)行鏈接則可完成構(gòu)建波岛,時間可以大大縮短。理論上音半,主工程拆分出來越多则拷,構(gòu)建時編譯時間就會越少。
通過以上的思路曹鸠,很容易就會走向多倉庫項目管理煌茬,Cocoapods一上,項目管理得整整齊齊物延。
但是,項目的組件依賴往往不如圖2那么簡單仅父。如圖3叛薯,組件D依賴組件A和B浑吟,修改組件A需要重新編譯和發(fā)布組件A和組件D。(現(xiàn)實狀況會比圖3復(fù)雜的多得多)
雖然通過二進(jìn)制化組件可以減少編譯時間耗溜,但是一旦使用多倉管理這種重型項目管理方式组力,會使團(tuán)隊陷入了無止境的編譯發(fā)布流程,光是Debug的工作就是噩夢般的存在抖拴。小項目燎字,小團(tuán)隊使用多倉管理,等于把自己推向深淵阿宅。
-
單倉多組件全量編譯工作量過大
單倉多組件沒有組件發(fā)布流程候衍,debug時也像無組件工程一樣來去自如。這種組件化方式卻無法縮短編譯時間洒放,只得到了分層的好處蛉鹿。
當(dāng)然,還可以做成單多倉結(jié)合往湿,業(yè)務(wù)組件修改頻繁妖异,放在主倉庫,底層穩(wěn)定組件封裝领追,放在分倉庫他膳。這種方式能解決部分問題,但是編譯時間還是冗長绒窑,畢竟業(yè)務(wù)組件基本都是大頭棕孙。
如圖4,當(dāng)組件A修改后回论,理論上只需要重新編譯A散罕、D和主工程,但是實際操作上傀蓉,一個Clean動作就把組件B欧漱、C的編譯產(chǎn)物也清理了。也就是說葬燎,在單倉情況下误甚,全量編譯的情況是非常普遍的。
單倉多組件二進(jìn)制化
所以谱净,是否存在一種方案窑邦,在單倉多組件的項目管理方式下,解決如圖4出現(xiàn)的壕探,對組件B和組件C的冗余編譯工作冈钦?
毫無疑問,理論上李请,這肯定是存在瞧筛,因為構(gòu)建的工作就是這樣執(zhí)行的厉熟。圖4的情況下,保留組件B和組件C的編譯產(chǎn)物就可以做到较幌。
因此揍瑟,我們需要對工程進(jìn)行改進(jìn),把編譯產(chǎn)物二進(jìn)制文件緩存下來乍炉,并使用它們绢片。(其實整體思路有點像增量編譯那套,只是緩存的顆粒大一些)
如圖5岛琼,通過對二進(jìn)制產(chǎn)物緩存起來底循,并在項目中鏈接使用該產(chǎn)物,則可以實現(xiàn)衷恭。雖然看似簡單此叠,但實際上是需要將原來交由Xcode托管的依賴管理機制重新實現(xiàn)一遍。
4.依賴計算以及編譯順序
如圖5中随珠,修改了組件A灭袁,需要計算出需要重新編譯的組件和順序是:A、D窗看。因此茸歧,若實施方案,則需要先梳理好所有組件的依賴關(guān)系显沈,而不是像原來一樣交個Xcode計算软瞎。確定好需要重新編譯的組件后,安裝依賴關(guān)系對組件按順序重新編譯拉讯。
5.增量編譯兼容
實際工作中涤浇,需要執(zhí)行增量編譯。比如魔慷,上一次編譯中只锭,修改了組件A、D院尔,也編譯了組件A蜻展、D,后續(xù)又修改了D邀摆,則本次只需執(zhí)行編譯組件D纵顾,而不是組件A、D栋盹。而且施逾,組件D執(zhí)行的是增量編譯,秒級的,而不是全量編譯汉额。筆者的方案是通過git diff進(jìn)行hash計算修改沪饺,并使用Xcode的自身的增量編譯。
實踐
·基礎(chǔ)知識
- Xcode的依賴管理
Xcode的依賴管理分為兩種闷愤,第一種是顯式依賴(Dependencies),第二種是隱式依賴(Implicit Dependencies)件余。
- 顯式依賴(Dependencies)的管理放在了Build Phases下的Dependecis欄讥脐,開發(fā)者可以手動添加依賴,僅限workspace下的target
- 隱式依賴(Implicit Dependencies)是XCode在構(gòu)建時進(jìn)行計算得出的啼器,具體計算范圍是顯式依賴基礎(chǔ)上旬渠,疊加project下被鏈接的庫(Link Binary With Libraries 以及 手動鏈接的庫)
- Scheme、Target
Scheme是管理各種構(gòu)建等配置的地方端壳,其中有一項是隱式依賴的開關(guān)
Target是管理構(gòu)建信息的地方告丢,普通項目的target對應(yīng)一個構(gòu)建產(chǎn)物
Scheme和Target的概念大家可以網(wǎng)上找找,此處不贅敘损谦。
·具體方案
整體構(gòu)建流程:
Step0. Scheme處理
·為了不影響原構(gòu)建方案岖免,需要額外新建一個主工程的scheme,直接Xcode上new scheme照捡,選擇主工程(todo: 配圖 打碼)
·修改所有project的scheme颅湘,取消所有工程的Find Implicit Dependencies,從而取消Xcode的隱式依賴管理栗精。
Step1. Target處理
·新建一個target闯参,直接對原主工程target duplicate一個,并完善info.plist的指向
·新建一個target悲立,選擇腳本(本方案通過腳本執(zhí)行組件的依賴管理以及構(gòu)建工作)
·修改目標(biāo)工程的依賴鹿寨,新增腳本依賴
Step2. 緩存構(gòu)建產(chǎn)物
·項目中新建文件夾對產(chǎn)物進(jìn)行緩存,修改.gitignore
·主項目和子項目framework的鏈接地址(Link binary with Libraries)全部指向緩存文件夾
·通過腳本完成編譯的組件二進(jìn)制產(chǎn)物放到緩存文件夾(切記使用cp命令薪夕,不要使用mv命令脚草,否則Xcode增量編譯失效)
Step3. 依賴關(guān)系梳理(腳本實現(xiàn))
Step4. 計算出需要編譯的組件(腳本實現(xiàn))
· 緩存文件夾中無的組件需要編譯
· 被修改的組件需要編譯
· 組件中依賴的組件被編譯的需要重新編譯
Step5. 執(zhí)行構(gòu)建(腳本實現(xiàn))
Step6. git diff 文件hash計算
每個git diff的hash值對應(yīng)一個二進(jìn)制文件產(chǎn)物,通過hash值的比對來確定修改是否已經(jīng)編譯過寥殖。