让编译和打包飞起来 ---打包、持续集成速度优化

随着app体量增大,组件多,依赖多,源码多,编译、打包已经变成一个沉重的工作,我们产品一次全量编译大约需要20分钟,jenkins上面的全量编译需要80分钟。

实在不能忍!

下面通过一张图简要列举下,打包时时间主要消耗在哪些方面

分析一下:

  • 索引:
    源文件之间引用关系等,初次打开工程之前一般xcode需要一点时间进行索引操作

  • 编译:
    主要对每个源文件编译、优化为机器代码,所以此处跟文件数量有关

  • 链接:
    根据文件之间的依赖关系,将依赖的系统库和其他库链接到一起

  • 下载源码:
    针对现在大多数工程依赖pod来管理源码,一般需要下载公开pod源的三方源码和公司内部私有pod源的二方源码,以及用的图片、音视频、配置等资源文件

  • xcode配置:
    有些配置会导致编译过程需要额外的操作而浪费时间,例如生成dSYM是为了方便定位crash时候的符号表,但我们编译阶段一般不需要此选项。

  • 磁盘IO
    打包整个过程中涉及多个IO操作,例如将整个工程从磁盘读到内存、打包时将可执行文件、资源文件等打包到一起拷贝到磁盘中。


针对以上分析,简要将优化方案分为三方面:

xcode配置

  1. 首先是xcode配置中的架构architecture:不管是debug和release都可以考虑配置为build active architecture。 这样至少可以减少40%的编译时间,尤其是在兼容性测试之前,一般项目兼容性测试之前,测试和开发同学的手机节本都是iphone6以后机型,也就是说除了兼容性测试,一般用不上arm7s armv7 两种架构。


  2. 其次是是生成dSYM文件:由于dSYM文件存放是编译打包时的工程中字符信息,用于重现方法内部字符、方法之间的引用关系,所以配置为debug期间不需要,如下图:


  3. 最后是否优化:是否优化代码决定了代码的执行效率,app包大小。 所以建议发布到appstore之前,debug时不做优化,便于调试;release时做最低优化:


其他还有一些对打包编译有影响的选项不列举了,影响不大,并且默认选项就可以。

工程和源码

1. 瘦身

工程和源码方面,我们需要考虑到工程的源文件数量、资源数量不止对app大小有影响,也会影响打包速度,例如一些删减了的功能点对应的源文件,可能涉及引用很多其他源文件、三方库、图片资源,这些文件和库都要对编译和链接过程增加负担,所以建议先对app进行瘦身,可以参考燃烧app的卡路里--app瘦身之路

2. 整理合并压缩三方库,和基础二方库

对应很多三方库,我们通常是通过pod形式直接引用源码到工程中,这就带来了很多重复工作量,包括每次pod install时重新下载源文件、每次编译时重复编译源文件,但由于三方源码一般更新版本频率很低,而且只有在我们发现重大bug有更新或性能提升时才有必要更新,所以此处建议将常用三方源码统一打包到基础组件中打包成.a静态库,如afnetworking cocoa fmdb yykit 这些,他们组成一个统一三方组件,每次我们只需要下载整个组件的.a库,编译时无需再到git仓库拉取代码,无需重复编译工作,对下载、索引、编译都有很大提升。

另外公司内部其他组件,如果更新频率很低(例如每个月更新一个版本)或者只有一个地方依赖它,这时也可以考虑将此组件以库的形式引用到工程中。而源码由对应团队自己维护,并定期将更新的库push到组件中。例如我们工程中的一些基类组件,网络组件等,除非需要修复bug,其余时间基本没有更新代码,就可以这样做。

3. 脚本优化

a. 引入CCache

通过jenkins持续集成时,会忽略缓存,每次都是重新全量编译,为了解决这个问题可以引入CCache,缓存编译的中间产物,这样有效的增量编译可以极大提高效率。

b. 分支隔离

虽然引入CCache有效利用了上一次编译结果,但是如果每次编译前后库的分支都有变化怎么办? 最明显的例子就是我们在小版本的发布和大版本开发两个时间有交织的时候,此时我们通常基于同一个主线工程,会经常有release分支的打包和develop分支打包的交错进行,但是这两个分支的podfile其实存在很大差异,前者一般是依赖各个组件的release分支或master分支,而后者一般依赖develop分支。 这种交错进行实质上pod的下载和编译都没法重复利用,此时CCache也没用!

我们分析下这个问题:此时两个分支虽然工程物理上基于同一个,但其实下载组件和编译源码时并没有关系,基于此特点,我们可以将develop分支 独立为一个工程,另一个release独立为一个工程,二者采用独立的路径、podfile,这样每次我们在develop分支的路径下都只会打此分支的包,而release分支下只会打release分支的包,两个分支在不同路径下,所以相互独立,而各自又可以有效地利用CCache缓存结果提高效率

硬件层面

硬件层面主要是考虑到IO操作、cpu编译操作、网络传输操作给打包带来的问题,我们可以考虑一台性能强劲的服务器,装上macos系统,化身黑苹果! 或者直接使用“垃圾桶“(苹果自家的服务器),主要是配置多核cpu,ssd,并且将打包机器部署在本地,减小包文件传输成本。


推荐阅读更多精彩内容