Another way of thinking about building compilation acceleration for iOS

1. Background


I believe that every team will have such problems. When the company's business gradually grows, your project code must also grow rapidly. Since the release of the iOS version, hundreds of versions have been released. The entire project is designed in components, and the private libraries and third-party libraries used have reached 130+. This large-scale project has brought us many problems:

  1. The project compilation speed is slow, and the complete compilation is more than 20 minutes.

  2. The output packaging speed is slow. When the development is completed and the test is submitted, the packaging is more than 20 minutes at a time.

When faced with numerous business requirements and complaints, we knew that this problem could not wait and needed to be solved urgently.

Under certain resource conditions, it is impossible to use external hardware conditions to improve and improve, so how to solve this problem at the technical level has become an urgent need for the team to solve.

2. Research

1. Xcode compilation optimization


As the official tool for our iOS development, if Xcode itself has a way to improve the speed, it is of course the best, but after the research and attempt, it is indeed possible to change some parameters to improve some speed, but it is not obvious, some parameters change even at the expense of other things. I won't go into details here, the relevant information is already very rich

2. CCache etc.


In this category, the results of the previous compilation are cached by certain means and reused next time to achieve the purpose of reducing compilation time. However, there are some limitations at present, which cannot be accessed in a friendly and simple manner. Some projects may need to make some changes to achieve the function of caching. This is not a detailed introduction, related content can be found on the official website of each project

3. Binary


I believe this should be the most used solution in the industry. The current popular componentization in the industry is to binarize each component library to avoid repeated compilation each time, so as to reduce the compilation time. For the overall detailed plan, all teams in the industry also output suitable projects for their own projects. Here are the approximate steps:

  1. make a plugin

  2. Compile the binary version of the component library

  3. Component source code and binary, and push it to the remote end at the same time

  4. In the stage of generating the project, to determine whether the sub-library refers to binary or source code

  5. To pull the corresponding version, switch the source binary tool

As can be seen from the above steps, there are several problems here:

  1. The project must already be componentized

  2. Problems with binary component libraries (how to binary, when to binary, storage problems caused by binary, pull speed problems, Xcode index duration problems, debugging problems)

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

三、基于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流程优化

  • 分支切换缓存

  • 编译缓存

Guess you like

Origin juejin.im/post/7040792078489485348