前言
本文会对 clang driver
的内部流程做一个简单的介绍。
clang driver 流程简介
clang Driver
负责拼接编译器命令和 ld
命令。
注意:
clang driver
自身不会对源码进行编译
clang Driver
的处理逻辑分为以下几步:
-
Parse: Option Parsing:解析传入的参数
-
Pipeline: Compilation Action Construction:根据每个输入的文件和类型,组建
action
(比如PreprocessJobAction
)可以通过
clang -ccc-print-phases
可以查看需要处理的action
1 2 3 4 5 6 7 8 9 10 11 12 13
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
可以查看对应的工具和文件名信息1 2 3 4 5 6 7
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
参数为例:原始命令:
1
xcrun --sdk iphoneos clang -arch arm64 main.m -v
各个
tool
的参数:usr/bin/clang -cc1
的参数:-triple arm64-apple-ios14.4.0
usr/bin/ld
的参数:-arch arm64
-
Execute:调用不同的
tool
执行任务。该步骤会通过创建子进程方式调用
tool
仍然以
xcrun --sdk iphoneos clang -arch arm64 main.m -v
为例,clang driver
最终会创建两个子线程clang -cc1
和ld
执行最终的编译任务和链接任务-
clang -cc1
可以将源码转为对象文件。本例中,clang -cc1
会将.m
文件转为.o
文件 -
ld
负责将多个对象文件、库进行链接操作,并产出最后的产出文件。本例中,ld
会产出 可执行文件
-
为了方便理解,我们可以将下面的图片和上面的流程对应:
更多相关内容可以参考笔者之前的文章 Ruby 与 clang 的 “Clang driver” 部分 或者 Driver Design & Internals
clang driver 源码概览
首先,我们以 xcrun -l clang main.m -v -O2 -o demo
为例对整个流程做一个简单的介绍
- 第一步:
clang
会以driver
模式被调用 - 第二步,
clang driver
会根据传入的main.m
参数构建为两个 Job- 第一个任务是编译任务,
clang
接收-cc1
参数后会以编译器的身份执行编译任务,输入文件是main.m
,输出文件是main.o
对象文件 - 第二个任务是链接任务,
ld
会将main.o
链接为demo
可执行文件
- 第一个任务是编译任务,
- 最后,会根据上面的两个
Job
创建新的进程执行上面的两个Job
正式分享前,我们先对本文涉及的 流程图 进行初步分享。读者可以结合流程图辅助记忆。
注意:随着源码的逐渐分析,流程图的细节会被逐渐完善
|
|
-
main
函数会先创建诊断 (DiagnosticsEngine
)实例诊断是编译器与开发者进行交互的重要部分。编译器通过诊断可以提供错误、警告或建议。
-
随后,开始创建
Driver
(clang::driver::Driver
) 的实例:TheDriver
,TheDriver
负责后续的clang driver
相关任务 -
通过
Driver
的BuildCompilation
方法生成需要执行的命令 -
当
Driver
构造完Jobs
后,会通过Driver
的ExecuteCompilation
方法执行命令
构建 Compilation
下面,我们再对 BuildCompilation
的流程进一步拆解。
BuildCompilation
方法主要包含以下步骤:
-
参数处理
调用
ParseArgStrings
函数 处理程序接收的参数 和 对配置文件解析 -
通过
computeTargetTriple
函数获取triple
并通过getToolChain
函数获取对应的ToolChain
getToolChain
函数逻辑比较简单,对于iOS
开发者,该函数会返回根据triple
的系统信息返回DarwinClang
的实例 -
创建
Commpilation
持有参数 -
通过
BuildInputs
函数获取输入文件clang driver
支持一次性编译多个文件,比如下面的命令可以同时编译main.m
和test.m
两个源码文件1
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
-
types::ID types::lookupTypeForExtension(llvm::StringRef Ext)
函数会根据输入文件main.m
的扩展名m
获取该文件的类型TY_OBJC
-
输入文件处理完成后,会通过
BuildUniversalActions
函数构建Action
-
随后再通过
BuildJobs
函数构建Jobs
经过本节分析后,我们的流程图节点也膨胀了一倍,并且能够和之前分享的 clang driver
流程图片基本一一对应。
|
|
本文总结
本文结合实际的例子,对 clang driver
流程进行了简单的分享。
下一篇开始,我们会对 clang driver
的各种细节逐一介绍。