前言: 主要涉及OC工程中的部分编译相关内容,更多更具体的内容建议查看参考文献
- (一): 介绍构建系统
- (二): 规范实践
- (三): 头文件映射及模块映射
- (终): 增量构建优化技巧
xcode构建系统
摘要: 注意介绍一下构建系统流程及其有向图,明确编译任务之间的依赖关系
通过一系列有序的任务,将源代码和资源转化为产品app;
常见的任务包括编译,链接,文件操作等等;
build,run,test,profile,analyze,archive都会调用构建系统。
默认情况下,xcode会并行化构建,充分利用系统硬件的优势;
如下位置可查看:xcode -> scheme -> Build -> Paralleliza Build
构建系统工作流
- 决定必须构建哪些target
- 检索构建配置,设置,阶段,规则列表
- 检查对其他目标的依赖关系并构建这些目标
- 准备必须执行的构建任务的完整列表
- 如果可以进行增量构建,则准备增量构建任务列表
- 执行构建任务,这涉及到通过所有构建阶段。 对于每个构建阶段,在必要时应用自定义构建规则
- 生产产品
如下流程图:
在 准备必须执行的构建任务的完整列表 阶段后,生成如下有向图:
- 构建任务的执行顺序是依照其依赖顺序决定的
- 建立依赖的根源是任务的input及output
- 根据依赖选择串行还是并行
有向图可以从4个层次进行抽象分析:
- app
- target
- build phases
- 源文件依赖(.m)
app:依赖主工程link完毕的可执行文件及相关资源文件,配置;
target:指明target之间的依赖关系,以便构建系统以正确且高效的顺序进行;
xcode提供了2种方式定义target的依赖关系:
- implicit via Link Binary and Libraries(隐式依赖)
- explicit via target dependencies (指定依赖)
implicit via Link Binary and Libraries(隐式依赖):如果 scheme设置 Find Implicit Dependencies 勾选,所有添加在当前target的Link Binaries With Libraries构建阶段中的framework or library都会建立与当前target的隐式依赖;
explicit via target dependencies (指定依赖):
target -> Build Phases -> Dependecies 指定当前target的依赖target;
PS: framework and libraries 通过build setting 中 Other Linker Flags不会建立隐式依赖,需要手动指定依赖关系
拿cocoapods举个例子:
target的编译构建必须等待其依赖的target编译构建之后才能进行;
PS:可通过在target的build phases阶段插入脚本,脚本内容是向同一个文件中追加文本来确认这种依赖顺序;
总结:
- 通过显式指定依赖或者隐式依赖管理依赖模型,方便编译器高效且正确的运行,充分利用多核并发优势;
- 制作组件库的时候,考虑粒度职责划分,公共部分单独抽取出来
Build Phases:通常情况下根据各阶段的排列顺序,按组运行,如果有更好的处理方法,也会忽略这个执行顺序,例如link libraries
源文件依赖:
target开始编译源(.m)文件之前,已经完成.h文件的检索,并将其路径写入hmap文件中;
源文件的编译是并行的
相关概念:
增量构建:基于上次对项目的修改,只执行定向图上的相关任务子集
任务签名:
1. 构建图中的每个任务都有一个签名
2. 根据input的统计信息和其他任务元数据计算签名
3. 构建系统跟踪当前和以前构建的任务签名
4. 比较以确定是否应运行任务
复制代码
增量构建中的依赖模型
- oc中增量构建是基于头文件
- swift中增量构建是基于文件
构建系统编译到源文件的时候,会根据导入的头文件,生成对应的.d文件(dependencies缩写),.d文件中列出源文件在增量构建中的所有依赖;当依赖的文件修改的时候,会触发源文件的rebuild;
.d文件路径:复制.m的编译日志到终端,加上 -v,输入内容中 (-dependency-file) 对应的就是.d文件存储路径;
复制代码
.d文件通常包括如下几种类型:
- modulemap
- pch
- .h
- self.m
在基于 C 的语言中,导入头文件(递归操作)通常会将头文件的内容复制到您的源文件中;.d文件包含所有导入的依赖;
优化思路:递归操作会导致当前的源文件依赖所有导入的头文件,但其中有部分头文件是当前源文件不需要依赖的,这部分的多余依赖链是应该被优化掉的
优化实践:
- .h中通过@class声明, 告诉编译器存在这个类,.m中再导入头文件,这样可以切断多余的依赖链
总结:
- 理解构建系统流程图及有向图