[译]理解 Xcode 构建系统

原文连接:www.vadimbulavin.com/xcode-build… by Vadim Bulavin前端

翻译:CoderWangxgit

每个 Swift 程序在真实设备上运行前都要经历一系列转换。这个过程一般是由 Xcode 构建系统处理的。在这篇文章中咱们将了解 Xcode 构建系统的各个部分。程序员

问题描述

任何计算机系统都包含两方面:即 软件硬件github

硬件 是计算机的物理部分,例如显示器、键盘。硬件 一般由 软件 控制,软件 是指导硬件如何工做地一系列指令集合。软件负责编排过程,硬件负责实际执行工做,缺一不可。编程

做为软件工程师,咱们主要关注软件部分。然而,硬件并不能直接理解使用 Swift 编写的代码,它只能接收电荷形式的指令,包含两个级别,分别称做*‘逻辑 0’‘逻辑 1’*。swift

这里就有个问题:“如何将 Swift 代码转换成硬件能接受的形式”? 答案是语言处理系统后端

语言处理系统

语言处理系统 是一系列程序的集合,这些程序能够从一组用任意源语言编写的指令中生成可执行程序。这样就容许程序员使用高级语言而不用去写机器代码,大大下降了编程复杂度。xcode

咱们在 iOS 或 macOS 开发中平常使用的语言处理系统就称做 Xcode 构建系统app

Xcode 构建系统

Xcode 构建系统 的主要目标是协调各类不一样任务的执行,最终生成一个可执行程序。编程语言

Xcode 运行许多工具,并在它们之间传递数十个参数,处理执行顺序、并行性等等。这确定不是你在编写下一个 Swift 项目时想要手动处理的。

多数语言处理系统,包括 Xcode 构建系统,都包含 5 个部分:

  • Preprocessor(预处理器)
  • Compiler(编译器)
  • Assembler(汇编器)
  • Linker(连接器)
  • Loader(加载器)

它们经过以下图所示方式协做:

language processing system

让咱们仔细了解一下各个步骤。

Preprocessing 预处理

预处理步骤的目的是将程序转换为能够被提供给编译器的形式。它将宏替换为具体定义,发现依赖项并解析预处理器指令。

考虑到 Swift 编译器中没有预处理器,因此不容许在 Swift 项目中定义宏。尽管如此,Xcode 构建系统 仍是进行了部分补足,经过在项目构建设置中配置 Active Compilation Conditions (主动编译条件) 方式来进行预处理。

Xcode 经过低级构建系统 llbuild 来解析依赖项,llbuild 是开源的,能够在 Github 上的 swift-llbuild 页面 找到更多信息。

Compiler 编译器

编译器 是一个程序,它将一个语言的源程序映射为另外一个语言中语义等效的目标程序。换句话说,编译器SwiftObjective-CC/C++ 代码转换为机器码而不丢失前者的含义。

Xcode 使用两个不一样的编译器:一个负责编译 Swift,另外一个负责编译 Objective-CObjective-C++ 以及 C/C++ 文件。

clang 是苹果官方的 C 语言家族编译器,已经开源:swift-clang

swiftc 是一个 Swift 编译器程序,被 Xcode 用于编译及运行 Swift 源代码。我冒昧地猜想你已经访问过这个连接至少一次:它位于Swift 语言仓库

编译器 阶段以下图所示:

Xcode compiler

编译器由两个主要部分组成:前端和后端。

前端 部分将源程序分割为单独的部分,没有任何语义或类型信息,使用特定语法结构。而后编译器使用这个结构生成源程序的 中间描述(intermediate representation)前端 也会建立并管理 符号表(symbol table),以搜集源程序相关信息。

符号(Symbol)是数据或代码片断的名称。

符号表 存储你所命名的变量、方法、类的名称,每一个 符号 都映射到一个肯定的数据块。

Swift 编译器中间描述(intermediate representation) 被称做 Swift 中间语言 Swift Intermediate Language (SIL)SIL 会被用于后续的分析及代码优化。直接从 Swift 中间语言 生成机器码是不可能的,所以 SIL 会再通过一次转换变为 LLVM 中间描述(LLVM Intermediate Representation)

后端 阶段,以上LLVM 中间描述 会被转换为汇编码。

Assembler 汇编器

汇编器 将可读的汇编代码转换成 可重定向的机器代码(relocatable machine code),生成 Mach-O 文件,基本上就是代码与数据的集合。

上述定义中的术语:机器代码* 和 Mach-O 文件 还须要进一步解释。

机器代码 是一种数字化语言,表示一组可由 CPU 直接执行的指令。之因此命名为可重定向的,是由于无论对象文件在地址空间中何处,指令都会相对于所在空间来执行。

Mach-O 文件 是 iOS / macOS 操做系统中的一种特殊文件格式,用于对象文件、可执行文件及库。它是以一些有意义的块分组的字节流,运行于 iOS 设备上的 ARM 处理器或者 Mac 上的 Intel 处理器。

Linker 连接器

连接器 是一个计算机程序,它将不一样的对象文件和库合并起来生成一个能够在 iOS 或 macOS 系统上运行的 Mach-O 可执行文件。连接器 接收两种类型的文件做为输入,也就是来自 汇编 阶段的对象文件以及不一样类型的库( .dylib, .tbd , .a)。

细心的读者可能已经注意到 汇编器连接器 都生成了一个 Mach-O 文件做为输出。这二者应该有些不一样,对吧?

来自汇编阶段的对象文件并无处理完成,其中一些包含引用其余对象文件或库的缺失部分。举个例子,若是在代码中使用了 printf 方法,是 连接器 将这个符号与实现 printf 方法的 libc 库粘合起来的。它使用 编译 阶段生成的 符号表 来解析跨不一样对象文件与库之间的引用。

Xcode 中构建具备上述特性的 Swift 项目时,你能够已经发现过 “undefined symbol 未定义符号” 错误。

Loader 加载器

最后,做为操做系统一部分,加载器 会将程序载入内存并执行。加载器分配运行程序所需内存空间并将寄存器初始化为初始状态。

总结

在软件工程中很难低估 语言处理系统 的重要性。咱们能够自由选择几乎任何高级编程语言,例如 SwiftObjective-C,而不用去编写硬件才能理解的 0 和 1 二进制代码。语言处理系统会处理其他工做,生成一个能够在 iPhone、Mac 或其余任何终端运行的可执行程序。

做为 iOS / macOS 开发者,咱们平常基础工做中都在使用 Xcode 构建系统。它的主要组件有:preprocessor 预处理器compiler 编译器assembler 汇编器linker 连接器、以及 loader 加载器Xcode 针对 SwiftObjective-C 使用不一样的编译器,对应分别是 swiftcclang

理解 Xcode 编译过程是基础知识,对于初学者和经验丰富的开发人员都很是重要。


感谢阅读

若是你以为不错,能够在 Twitter 上关注原做者;若是发现问题或有更好建议,欢迎留言或经过 Github 与我讨论。

相关文章
相关标签/搜索