[toc]
前言
本文会对 clang driver
的 参数解析 流程进行分享
为了控制 clang
的运行,clang
必须支持不同的参数对各种行为进行控制,所以,clang driver
启动后的第一个主要任务就是 参数解析
正式分享前,我们先按照惯例分享本文涉及的主要 类图 和 流程图,方便对 参数解析 的主要流程进行理解
- Info 是保存了预定义的各种
Option
信息的结构体。比如-v
参数的帮助信息是Show commands to run and use verbose output
- Option 是持有
Info
和OptTable
,提供了一些封装好的方法,比如通过OptionClass getKind()
方法暴露Info
的类型 - Arg 持有了
Option
和其它命令行参数信息,比如-arch armv64
的arm64
会被保存到Arg
- OptTable 提供解析参数,并懒加载创建
Option
的相关方法 - InputArgList 持有了输入的原始参数和解析后的参数列表
- DriverOptTable 记录了
clang driver
相关的Info
信息,是OptTable
的子类
|
|
|
|
一、DriverOptTable
DriverOptTable 记录了 clang driver
相关的 Info
信息,是 OptTable
的子类
-
DriverOptions
模块提供了函数const llvm::opt::OptTable &clang::driver::getDriverOptTable()
可以获取clang driver
支持的所有参数信息DriverOptTable
初始化时依赖的InfoTable
参数是通过clang/Driver/Options.inc
生成的通过下图,我们可以看到 InfoTable 的长度是 2776
小知识:当我们编译
llvm
项目时,会由TableGen
工具将Options.td
文件生成Options.inc
原始的文本信息如下:
-
因为
DriverOptTable
继承自OptTable
,所以,这里会触发OptTable
的初始化方法OptTable
的初始化时,会记录一些关键的 ID,用于后续使用,比如TheInputOptionID
- 同时,会通过
PrefixChars
和PrefixesUnion
记录合法的参数前缀,用于后续的快速参数合法性判断,比如-v
参数的前缀是-
二、Driver::ParseArgStrings
Driver::ParseArgStrings
方法的作用是将字符串数组解析为 ArgList
,并做相关的校验
具体流程如下:
-
调用
Driver::getOpts
获取clang driver
支持的所有参数Info
-
调用
ParseArgs
解析命令行参数 -
对解析到的命令行参数进行判断,检测到 不支持 或者 未知 的参数时,会抛出异常
如何区分 不支持
或者 不认识
的参数
-
clang driver
不支持 的参数,都可以通过Options.td
文件查到以
-pass-exit-codes
为例,gcc
支持该参数,但是clang
不支持 此参数 -
不认识 的参数就是类似于
-test
这种,开发者随意拼写的参数
三、ParseArgs
OptTable::ParseArgs
方法负责将字符串数组解析为 ArgList
具体流程如下:
- 先初始化
InputArgList
的实例,并存储原始的入参信息 - 通过
while
对原始参数字符串进行遍历,并通过OptTable::ParseOneArg
方法将所有的原始参数字符串解析为Arg
的实例 - 最后
Args
会持有所有的解析后的参数
通过添加调试代码,我们可以感受一下以下命令行对应的原始参数和解析后的 Arg
实例分别是什么样子
|
|
四、ParseOneArg
OptTable::ParseOneArg
方法负责解析单个参数
具体流程如下:
-
先移除参数的前缀,并通过
std::lower_bound
查找第一个前缀匹配的Info
比如,
-arch
会变成arch
-
根据
Info
初始化Option
持有参数信息 -
通过
Option::accept
方法校验参数是否正常 -
参数正常时直接返回
-
如果没有找到合适的参数,再判断参数是否以
/
开头,如果开始,会把参数当做源码文件进行处理 -
其它情况下,会当做参数当做 未知参数 进行下一步处理
-
std::lower_bound
会依赖下面两个方法查找第一个前缀匹配的参数Info.Name
和Name
的查找逻辑比较复杂,需要深入研究的同学,可以逐步调试帮助理解 -
Option::accept
方法会依次进行以下处理- 先转发到
Option::acceptInternal
方法进行参数校验 - 判断解析到的参数是否属于别名
- 如果别名,会进行特殊处理
比如,
-fembed-bitcode-marker
就是-fembed-bitcode=marker
参数的别名,两个参数的意义完全相同 - 先转发到
-
Option::acceptInternal
方法会根据Option
的类型进行处理并生成Arg
实例。因为
-arch
的类型是SeparateClass
,所以,会将下一个原始参数字符串(arm64
)当做value
进行处理类型 示例 Separate -arch arm64 Flag -v Joined -fembed-bitcode=marker
总结
本文通过分析 DriverOptTable
的生成机制并分析Driver::ParseArgStrings
内部流程,对 clang driver
的参数解析流程做了简单的分析