本文会对 clang driver
的内部流程做一个简单的介绍。
clang Driver
负责拼接编译器命令和 ld
命令。
注意:
clang driver
自身不会对源码进行编译
clang Driver
的处理逻辑分为以下几步:
Parse: Option Parsing:解析传入的参数
Pipeline: Compilation Action Construction:根据每个输入的文件和类型,组建 action
(比如 PreprocessJobAction
)
可以通过
clang -ccc-print-phases
可以查看需要处理的action
clang -ccc-print-phases -c t0.c t1.c
+- 0: input, "t0.c", c
+- 1: preprocessor, {0}, cpp-output
+- 2: compiler, {1}, ir
+- 3: backend, {2}, assembler
+- 4: assembler, {3}, object
5: bind-arch, "x86_64", {4}, object
+- 6: input, "t1.c", c
+- 7: preprocessor, {6}, cpp-output
+- 8: compiler, {7}, ir
+- 9: backend, {8}, assembler
+- 10: assembler, {9}, object
11: bind-arch, "x86_64", {10}, object
Bind: Tool & Filename Selection:根据 action
选择对应的工具和文件名信息
通过
clang -ccc-print-bindings
可以查看对应的工具和文件名信息
clang -ccc-print-bindings -c t0.c t1.c -arch arm64 -arch armv7
# "aarch64-apple-darwin19.6.0" - "clang", inputs: ["t0.c"], output: "/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/T/t0-9a2aac.o"
# "arm-apple-darwin19.6.0" - "clang", inputs: ["t0.c"], output: "/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/T/t0-7a4059.o"
# "arm-apple-darwin19.6.0" - "darwin::Lipo", inputs: ["/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/T/t0-9a2aac.o", "/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/T/t0-7a4059.o"], output: "t0.o"
# "aarch64-apple-darwin19.6.0" - "clang", inputs: ["t1.c"], output: "/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/T/t1-950abb.o"
# "arm-apple-darwin19.6.0" - "clang", inputs: ["t1.c"], output: "/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/T/t1-044386.o"
# "arm-apple-darwin19.6.0" - "darwin::Lipo", inputs: ["/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/T/t1-950abb.o", "/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/T/t1-044386.o"], output: "t1.o"
Translate: Tool Specific Argument Translation:根据输入的参数转为不同 tool
的参数
以 xcrun --sdk iphoneos clang -arch arm64 main.m -v
的 -arch arm64
参数为例:
原始命令:
xcrun --sdk iphoneos clang -arch arm64 main.m -v
各个 tool
的参数:
image
usr/bin/clang -cc1
的参数:-triple arm64-apple-ios14.4.0
Execute:调用不同的 tool
执行任务。
该步骤会通过创建子进程方式调用tool
仍然以 xcrun --sdk iphoneos clang -arch arm64 main.m -v
为例,clang driver
最终会创建两个子线程 clang -cc1
和 ld
执行最终的编译任务和链接任务
clang -cc1
可以将源码转为对象文件。本例中,clang -cc1
会将 .m
文件转为 .o
文件为了方便理解,我们可以将下面的图片和上面的流程对应:
DriverArchitecture
更多相关内容可以参考笔者之前的文章 Ruby 与 clang 的 "Clang driver" 部分 或者 Driver Design & Internals
首先,我们以 xcrun -l clang main.m -v -O2 -o demo
为例对整个流程做一个简单的介绍
clang
会以 driver
模式被调用clang driver
会根据传入的 main.m
参数构建为两个 Jobclang
接收 -cc1
参数后会以编译器的身份执行编译任务,输入文件是 main.m
,输出文件是 main.o
对象文件ld
会将 main.o
链接为 demo
可执行文件Job
创建新的进程执行上面的两个 Job
image
正式分享前,我们先对本文涉及的 流程图 进行初步分享。读者可以结合流程图辅助记忆。
注意:随着源码的逐渐分析,流程图的细节会被逐渐完善
main
函数会先创建诊断 (DiagnosticsEngine
)实例
诊断是编译器与开发者进行交互的重要部分。编译器通过诊断可以提供错误、警告或建议。
image
Driver
(clang::driver::Driver
) 的实例:TheDriver
,TheDriver
负责后续的 clang driver
相关任务
image
Driver
的 BuildCompilation
方法生成需要执行的命令
image
Driver
构造完 Jobs
后,会通过 Driver
的 ExecuteCompilation
方法执行命令
image
下面,我们再对 BuildCompilation
的流程进一步拆解。
BuildCompilation
方法主要包含以下步骤:
参数处理
调用 ParseArgStrings
函数 处理程序接收的参数 和 对配置文件解析
image
通过 computeTargetTriple
函数获取 triple
并通过 getToolChain
函数获取对应的 ToolChain
image
getToolChain
函数逻辑比较简单,对于 iOS
开发者,该函数会返回根据 triple
的系统信息返回 DarwinClang
的实例
image
创建 Commpilation
持有参数
image
通过 BuildInputs
函数获取输入文件
image
clang driver
支持一次性编译多个文件,比如下面的命令可以同时编译 main.m
和 test.m
两个源码文件
xcrun -l -sdk iphoneos clang main.m -arch arm64 -target x86_64-apple-ios8.0 test.m -v
BuildInputs
方法会遍历所有的参数,并筛选 Option::InputClass
类型的参数,最后会调用函数 types::ID types::lookupTypeForExtension(llvm::StringRef Ext)
获取对应的 types::ID
image
types::ID types::lookupTypeForExtension(llvm::StringRef Ext)
函数会根据输入文件 main.m
的扩展名 m
获取该文件的类型 TY_OBJC
image
输入文件处理完成后,会通过 BuildUniversalActions
函数构建 Action
image
随后再通过 BuildJobs
函数构建 Jobs
image
经过本节分析后,我们的流程图节点也膨胀了一倍,并且能够和之前分享的 clang driver
流程图片基本一一对应。
本文结合实际的例子,对 clang driver
流程进行了简单的分享。
下一篇开始,我们会对 clang driver
的各种细节逐一介绍。