Otra forma de pensar en la creación de aceleración de compilación para iOS

1. Antecedentes


Creo que todos los equipos tendrán esos problemas.Cuando el negocio de la empresa crece gradualmente, el código de su proyecto también debe crecer rápidamente. Desde el lanzamiento de la versión de iOS, se han lanzado cientos de versiones. Todo el proyecto está diseñado en componentes, y las bibliotecas privadas y de terceros utilizadas han llegado a más de 130. Este proyecto a gran escala nos ha traído muchos problemas:

  1. La velocidad de compilación del proyecto es lenta y la compilación completa dura más de 20 minutos.

  2. La velocidad de empaquetado de salida es lenta. Cuando se completa el desarrollo y se envía la prueba, el empaquetado dura más de 20 minutos a la vez.

Cuando nos enfrentamos a numerosos requisitos y quejas comerciales, sabíamos que este problema no podía esperar y necesitaba ser resuelto con urgencia.

Bajo ciertas condiciones de recursos, es imposible usar condiciones de hardware externo para mejorar y mejorar, por lo que la forma de resolver este problema a nivel técnico se ha convertido en una necesidad urgente para que el equipo lo resuelva.

2. Investigación

1. Optimización de compilación de Xcode


Como herramienta oficial para nuestro desarrollo de iOS, si Xcode en sí tiene una forma de mejorar la velocidad, por supuesto es la mejor, pero después de la investigación y el intento, es posible cambiar algunos parámetros para mejorar la velocidad, pero es no es obvio, algunos parámetros cambian incluso a expensas de otras cosas. No entraré en detalles aquí, la información relevante ya es muy rica.

2. CCache, etc.


En esta categoría, los resultados de la compilación anterior se almacenan en caché por ciertos medios y se reutilizan la próxima vez para lograr el propósito de reducir el tiempo de compilación. Sin embargo, existen algunas limitaciones en la actualidad, a las que no se puede acceder de una manera amigable y sencilla, algunos proyectos pueden necesitar hacer algunos cambios para lograr la función de almacenamiento en caché. Esta no es una introducción detallada, el contenido relacionado se puede encontrar en el sitio web oficial de cada proyecto.

3. binario


Creo que esta debería ser la solución más utilizada en la industria. La componente actual popular en la industria es binarizar cada biblioteca de componentes para evitar la compilación repetida cada vez, a fin de reducir el tiempo de compilación. Para el plan general detallado, todos los equipos de la industria también generan proyectos adecuados para sus propios proyectos. Estos son los pasos aproximados:

  1. hacer un complemento

  2. Compilar la versión binaria de la biblioteca de componentes.

  3. Código fuente del componente y binario, y empújelo al extremo remoto al mismo tiempo

  4. En la etapa de generación del proyecto, determinar si la sub-biblioteca se refiere a binario o código fuente

  5. Para extraer la versión correspondiente, cambie la herramienta binaria de origen

Como se puede ver en los pasos anteriores, hay varios problemas aquí:

  1. El proyecto ya debe estar por componentes.

  2. Problemas con bibliotecas de componentes binarios (cómo binario, cuándo binario, problemas de almacenamiento causados ​​por binario, problemas de velocidad de extracción, problemas de duración del índice Xcode, problemas de depuración)

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

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

  • 分支切换缓存

  • 编译缓存

Supongo que te gusta

Origin juejin.im/post/7040792078489485348
Recomendado
Clasificación