iOS关于构建编译加速的另一种思路

一、背景


相信每个团队都会有这样的问题困扰,当公司业务逐渐壮大,你的项目代码必然也是飞速增长。iOS版本自发布到现在,已经发布上百个版本,整个项目组件化设计,所使用的私有库,三方库已达到130+,这种大型项目给我们带来了诸多问题:

  1. 工程编译速度慢,完全编译一次20min以上

  2. 输出打包速度慢,在开发完成,提交测试阶段,一次打包20min以上

当面对众多业务需求提测,测试排队打包吐槽抱怨的时候,我们知道这个问题不能等了,急需解决。

在一定的资源条件下,无法利用外在硬件条件去提升改善,那么如何在技术层面去解决这个问题,成为摆在团队眼前急需解决的需求

二、调研

1.Xcode 编译优化


作为我们iOS的开发的官方工具,Xcode本身如果能有办法提高速度,那当然是最好的,但是调研尝试之后,确实可以通过一些参数的设置改变,提高一些速度,但不太明显,有些参数的改变甚至还要牺牲一些其他方面的东西。这里不去详细介绍,相关资料已经非常丰富

2.CCache等


这一类都是以一定的手段把上一次编译结果缓存起来,下一次重复利用,以达到减少编译时间的目的。但是目前都有一些限制,不能友好简单的接入,一些项目可能要针对的做出一些改变,以达到能缓存的功能。这样也不详细介绍,相关的内容可以去各项目官网去了解

3.二进制


相信这应该是目前业界使用最多的方案了,目前业界流行的组件化,把一个个组件库二进制化,避免每次重复编译,以达到减少编译时间的目的。整体详细方案,业界各团队也都输出适合自己项目的。这里介绍一下大概步骤:

  1. 制作插件

  2. 编译组件库的二进制版本

  3. 组件源码和二进制,同时推上远端

  4. 在生成工程的阶段,去判断子库是引用二进制还是源码

  5. 去拉取对应的版本,切换源码二进制工具

从上面步骤可以看出来,这里有几个问题:

  1. 工程要已经是组件化

  2. 二进制化组件库的问题(如何二进制,何时二进制,二进制带来的储存问题,拉取速度问题,Xcode index时长问题,调试问题)

从上面可以看出,这一套如果都解决好,肯定是需要一定的人力,时间,资源的,还是需要一定的成本。这一整套部署好,肯定还需要人去持续维护一段时间,直到这一套流程工具能稳定运行。

三、基于Xcode缓存的打包提速方案


综合上面介绍了几种方法,有的工程要改造,有的带来效果不理想,有的需要外部资源。那么有人会说了,我们

不想改造工程

不想部署一系列自动化工具,开发工具

公司iOS就是一个小团队,没有完善的开发环境

能不能有方法去提高打包编译速度呢?

有!一种基于Xcode本身的缓存方案,它来了:

  • 完全满足工程0入侵,你无需对现有工程做任何改动

  • 无需远端服务器,不必推送,拉取

  • 无需额外去开发其他工具辅助,独立或者团队开发没区别

1、想法

我们知道Xcode一次全量编译之后,当你再次编译时,它就会很快编译完成,因为它利用了上次编译的缓存,但是苹果采用的是文件时间戳去判断是否利用缓存,所以当我们pod update等操作之后,对文件有操作,很容易就引起了重复编译,虽然这时候这个文件可能并没有改动,这里不去推测为什么苹果只采用时间戳的方式去判断。所以我们很容易想到,能不能去利用一下苹果的这个缓存呢,当时自己写了个简单的工程去测试,把一次编译的中间产物拷贝出来,然后把工程clean,如果这时候build,理论上来讲这时候是要重新编译的,我在build之前把拷贝出来的上次编译产物给还原进去,这时候点build发现它并没有去重新编译我拷贝出来的库,并且运行结果也没什么问题。好家伙,好像这方法没什么问题!

2、实现

根据上面的想法,很容易想到的思路,我们在编译之前,通过一定的方法去判断当前需要编译的这个库是否有缓存,有缓存就用缓存,没有缓存就去编译,待整个工程的库判断完成后,开始编译,输出结果,然后把当前这次参与编译的库重新缓存起来。整体看起来很熟悉对不对,没错,它的整体思路和所有iOSer都熟悉的图片加载库SDWebImage基本一模一样。方案很熟悉,但是大家可能还是不是很清楚日和去实现,下面介绍一些大家可能关注的几个关键的步骤和问题:

  • 是否命中缓存?

  因为我们一般是关注文件内容改变与否,所以我们这里采用的是这个库的所有文件,以及其他一些定义的参数,去算出一个MD5,作为这个库的缓存key。这里可能会有疑问,文件那么多,这个计算速度会不会很慢?在我们实际工程中运行,实测并没有什么问题

  

  • 文中说,命中了就用缓存,那我怎么去控制xcode用缓存还是让它编译呢?

  这里大家可以去学习了解一下Xcode编译相关的内容,熟悉一下.pbxproj文件,这里简单介绍一下:

  一个 Xcode project 文件包含以下这些信息:

  - 源代码,包含头文件和实现文件

  - 内部和外部的静态库和动态库

  - 资源文件

  project.pbxproj 文件是 Xcode .xcodeproj 包里面的一个配置文件,我们修改 Project 和 target 里面的配置,实际上就是修改了 project.pbxproj。大家可以去打开这个文件看看,我们可以看到里面这样几个字段:

  PBXHeadersBuildPhase:用于framewrok构建的链接阶段

  PBXShellScriptPhase: 用于构建阶段复制资源文件的shell脚本

  PBXSourceBuildPhase: 构建阶段中编译源文件

  PBXResourceBuildPhase: 构建阶段需要复制的资源文件

  很明了,这几个字段不就是控制编译的吗,那么我们拿到这个target的pbxproj配置,去里面删除这几个字段,在编译的时候,Xcode读不到这几个字段,不就不重新去编译了吗。不必担心,这里Xcode都是提供了相关的接口让你去操作的,实际过程很简单

  

  • 缓存哪儿取,往哪儿拷贝?

  这里还是建议大家了解Xcode编译相关内容,Xcode编译过程有一系列环境变量,这几个字段大家可以了解下,下面几个字段基本囊括了编译过程中产物的一些路径:

  CONFIGURATION_BUILD_DIR: "/Xcode/DerivedData/project-dxdgjvgsvvbhowgjqouevhmvgxgf/ArchiveIntermediates/Project Distribution/BuildProductsPath/Distribution-iphoneos"

  CONFIGURATION_TEMP_DIR: "/Xcode/DerivedData/project-dxdgjvgsvvbhowgjqouevhmvgxgf/ArchiveIntermediates/Project Distribution/IntermediateBuildFilesPath/project.build/Distribution-iphoneos"

  TARGET_BUILD_DIR: "/Xcode/DerivedData/project-dxdgjvgsvvbhowgjqouevhmvgxgf/ArchiveIntermediates/Project Distribution/InstallationBuildProductsLocation/Applications"

  TARGET_TEMP_DIR:"/Xcode/DerivedData/project-dxdgjvgsvvbhowgjqouevhmvgxgf/ArchiveIntermediates/Project Distribution/IntermediateBuildFilesPath/project.build/Distribution-iphoneos/Project.build"

  CONFIGURATION_BUILD_DIR路径即为你需要取或者拷的位置,在编译过程中读取该字段的值,去把缓存拷贝到该路径,或者编译完成后,去该路径找到缓存取出来保存

  

  • 该如何让Xcode编译过程中如何去执行这些动作呢?

  Xcode对外提供了接口,编译过程中可以注入脚本,我们把需要执行的事件脚本编写好,通过Xcode的接口,注入到Xcode编译任务中。那么在后面编译每个target的时候,就会去执行我们自定义的脚本,从而去完成检测target缓存、拷贝等动作

3、运行

经过实测,在2019款MacBook Pro上,我们其中一个工程在完全编译的情况下时间大概850s左右,在使用上述方案后,完整命中缓存的情况下,时间可以低至60s,速度几乎是10倍以上的提升。当然我们实际打包输出,不可能每次都完整命中缓存,方案在线上运行的这段时间,我们统计到平均耗时为200s,提升了70%以上,这对于众多的分支,开发提测阶段,效率也是巨大的提升。

截屏2021-12-12 20.17.11.png 图中为我们现在主要两个工程使用该工具前后的耗时对比

4、可持续优化的地方

  • 缓存命中稳定问题,命中逻辑调优

  • 目前只能用于输出打包,适配到开发编译阶段

  • 目前只用本地缓存,可提供缓存同步

四、未来


目前整体方案以Ruby gem的方式输出,对APP工程完全0入侵,一行命令就可以轻松接入,未来计划将加入其它功能解决iOS开发中的痛点,以持续提升iOS开发的效率和体验

  • cocoapods流程优化

  • 分支切换缓存

  • 编译缓存

猜你喜欢

转载自juejin.im/post/7040792078489485348