原文地址:www.vadimbulavin.com/xcode-build…
原文作者:www.vadimbulavin.com/aboutme/
发布时间:2018年10月24日
每一个Swift程序在真正的设备上运行之前,都要经过一系列的转换。这个过程通常由Xcode构建系统来处理。在这篇文章中,我们将看看Xcode构建系统的每个部分。
任何计算机系统都是双面的:它有软件和硬件部分。
硬件是计算机的物理部分,如显示器或键盘。硬件通常由软件控制,软件是指令的集合,告诉硬件如何工作。由于软件协调过程,而硬件实际完成工作,所以两者都不能单独使用。
作为软件工程师,我们主要关注的是软件部分。然而,硬件并不能直接理解用Swift编写的代码。它只接受电荷形式的指令,包含两个层次,命名为 "逻辑0"和 "逻辑1"。
问题来了。"如何将Swift代码转化为硬件可以承受的形式"?答案是语言处理系统。
语言处理系统是一个程序的集合,它适合于从一组用任意源语言编写的指令中产生一个可执行的程序。它允许程序员使用更高级别的语言来代替编写机器代码,从而大大降低了编程的复杂性。
我们日常在iOS或macOS开发中使用的语言处理系统名为Xcode构建系统。
Xcode构建系统的主要目标是协调各种任务的执行,最终产生一个可执行的程序。
Xcode运行许多工具,并在它们之间传递几十个参数,处理它们的执行顺序、并行性以及更多的事情。这绝对不是你在编写下一个Swift项目时想要手动处理的事情。
大多数语言处理系统,包括Xcode构建系统,由5个部分组成。
这些部分以如下图所描述的方式共同发挥作用。
了解Xcode构建系统--语言处理系统
让我们仔细看看这些步骤中的每一个步骤。
预处理步骤的目的是将你的程序转换为可以提供给编译器的方式。它用宏的定义替换宏,发现依赖关系,并解决预处理器指令。
考虑到Swift编译器没有预处理器,我们不允许在Swift项目中定义宏。尽管如此,Xcode构建系统还是对其进行了部分补偿,并通过在项目构建设置中设置的Active Compilation Conditions来进行预处理。
Xcode通过低级构建系统llbuild来解决依赖关系。它是开源的,你可以在 swift-llbuild Github 页面上找到更多信息。
编译器是将一种语言的源程序映射成另一种语言的语义等同的目标程序的程序。换句话说,它将Swift、Objective-C和*C/C++*代码转化为机器代码,而不失去前者的意义。
Xcode使用两个不同的编译器:一个用于Swift,另一个用于Objective-C、*Objective-C++和C/C++*文件。
clang
是苹果公司针对C语言家族的官方编译器。它在这里开源:swift-clang。
swiftc
是一个Swift编译器可执行文件,Xcode使用它来编译和运行Swift源代码。我敢猜测,你至少已经访问过一次这个链接:它位于Swift语言库中。
编译器阶段描述在下图中。
了解Xcode构建系统 - Xcode使用两个编译器:clang和swiftc。
编译器由2个主要部分组成:前端和后端。
前端部分将源程序分割成没有任何语义或类型信息的独立片段,并对它们实施语法结构。然后编译器使用这个结构来产生源程序的中间表示。它还创建和管理收集源程序信息的符号表。
符号是代码或数据片段的名称。
符号表存储了变量、函数、类的名称,你可以叫它,其中每个符号都被映射到某个数据片段。
在Swift编译器中,中间表示被命名为Swift中间语言(SIL)。它是用来进一步分析和优化代码的。从Swift中间语言不能直接生成机器代码,因此SIL还要经过一次转换,变成LLVM中间表示。
在后端阶段,中间表示法被转化为汇编代码。
汇编器将人类可读的汇编代码翻译成可重新定位的机器代码。它产生的Mach-O文件基本上是代码和数据的集合。
上述定义中的机器代码和Mach-O文件术语需要进一步解释。
机器代码是一种数字语言,它表示一组可以由CPU直接执行的指令。它之所以被命名为可迁移,是因为无论该对象文件在地址空间的哪个位置,指令都会相对于该空间被执行。
Mach-O文件是iOS和macOS操作系统的一种特殊文件格式,用于对象文件、可执行文件和库。它是一个字节流,以一些有意义的块状方式分组,可以在iOS设备的ARM处理器或Mac上的Intel处理器上运行。
链接器是一个计算机程序,它可以将各种对象文件和库合并在一起,从而形成一个可以在iOS或macOS系统上运行的Mach-O可执行文件。链接器接受两种文件作为输入。这两种文件分别是汇编器阶段出来的对象文件和几种类型的库(.dylib
、.tbd
和.a
)。
细心的读者可能已经注意到,汇编器和链接器都会产生Mach-O文件作为其输出。它们之间一定有一些区别吧?
从汇编阶段出来的对象文件还没有完成。其中有一些包含了引用其他对象文件或库的缺失部分。例如,如果你在代码中使用了printf
,那么是链接器将这个符号与实现printf
函数的libc库粘合在一起。它使用编译器阶段创建的符号表来解决不同对象文件和库之间的引用。
当你在Xcode中构建你的Swift项目时,你可能已经偶然发现了 "未定义符号"错误,它具有上述性质。
最后,作为操作系统的一部分,加载器将程序带入内存并执行。Loader分配运行程序所需的内存空间,并将寄存器初始化为初始状态。
很难低估语言处理系统在软件工程中的重要性。我们不需要编写硬件能够理解的1和0的二进制代码,而是可以自由选择几乎任何更高级别的编程语言,比如说Swift或Objective-C。语言处理系统会完成剩下的工作,生成一个可以在iPhone、Mac或其他任何终端设备上运行的可执行程序。
作为iOS和macOS的开发者,我们每天都在使用Xcode构建系统。它的主要组件有:预处理器、编译器、汇编器、链接器和加载器。Xcode针对Swift和Objective-C语言使用了不同的编译器,对应的是swiftc和clang。
了解Xcode的编译过程是基础性的知识,对于初学者和经验丰富的开发者都有很大的意义。
如果你喜欢这篇文章,一定要在Twitter上关注我,不要错过任何新内容。
通过www.DeepL.com/Translator(免费版)翻译