酷酷的哀殿


  • 首页

  • 技术

  • 笔记

  • 杂记

  • Todo

  • 关于

  • 搜索
close

clang 源码导读(3): clang driver 参数解析

时间: 2021-03-02   |   阅读: 2430 字 ~5分钟

[toc]

前言

本文会对 clang driver 的 参数解析 流程进行分享

为了控制 clang 的运行,clang 必须支持不同的参数对各种行为进行控制,所以,clang driver 启动后的第一个主要任务就是 参数解析

正式分享前,我们先按照惯例分享本文涉及的主要 类图 和 流程图,方便对 参数解析 的主要流程进行理解

  1. Info 是保存了预定义的各种 Option 信息的结构体。比如 -v 参数的帮助信息是 Show commands to run and use verbose output
  2. Option 是持有 Info 和 OptTable,提供了一些封装好的方法,比如通过 OptionClass getKind() 方法暴露 Info 的类型
  3. Arg 持有了 Option 和其它命令行参数信息,比如 -arch armv64 的 arm64 会被保存到 Arg
  4. OptTable 提供解析参数,并懒加载创建 Option 的相关方法
  5. InputArgList 持有了输入的原始参数和解析后的参数列表
  6. DriverOptTable 记录了 clang driver 相关的 Info 信息,是 OptTable 的子类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
classDiagram


class Info {
  /
  // 前缀,比如 -,--,---
  const char *const *Prefixes;
  /
  // 参数名
  const char *Name;
  /
  // 帮助信息
  const char *HelpText;
  const char *MetaVar;
  unsigned ID;
  unsigned char Kind;
  unsigned char Param;
  unsigned int Flags;
  unsigned short GroupID;
  unsigned short AliasID;
  const char *AliasArgs;
  const char *Values;
}

class Option {
  const OptTable::Info *Info;
  const OptTable *Owner;
  - accept : Arg
  - acceptInternal: Arg
}

class Arg {
  /
  // 参数
  const Option Opt;
  /
  // 参数的值,比如 -arch armv64 的 arm64 会被保存
  SmallVector<const char *, 2> Values;
}

OptTable <|-- DriverOptTable

class OptTable {
  /// Option 信息
  std::vector<Info> OptionInfos;
  -parseArgs() InputArgList
  -ParseOneArg() Arg
}

ArgList<|--InputArgList
class ArgList {
  // 解析后的参数
  arglist_type Args;
}

class InputArgList {
  // 原始的参数数量
  unsigned NumInputArgStrings;
  /
  // 参数列表
  mutable ArgStringList ArgStrings;
}

class Driver {
  -ParseArgStrings() InputArgList

  -getOpts() OptTable
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
graph TD
    subgraph DriverOptions
      DriverOptTable初始化
      clang::driver::getDriverOptTable
    end

    subgraph OptTable
      OptTable初始化方法
      OptTable::ParseArgs
      创建InputArgList
      OptTable::ParseOneArg
      别名1(记录 TheInputOptionID 等信息)
      别名2(while 语句遍历)
      ArgList::append
    end

    subgraph Driver
      Driver::BuildCompilation
      Driver::ParseArgStrings
      别名5["遇到*不支持*的参数时,抛出错误"]
      别名6["遇到*不认识*的参数时,抛出错误"]
      Driver::getOpts
    end

    subgraph Option
      Option::accept
      Option::acceptInternal
      Option初始化方法
      别名3("创建 Arg")
      别名4("3.7 判断是否存在别名,并处理")
    end

main-->Driver::BuildCompilation--"1. 开始参数解析"-->Driver::ParseArgStrings--2.1 获取 OptTable-->Driver::getOpts--2.2 获取-->clang::driver::getDriverOptTable

--2.3 懒加载 DriverOptTable-->DriverOptTable初始化--2.4 调用父类OptTable的初始化方法-->OptTable初始化方法-->别名1(记录 TheInputOptionID 等信息)


Driver::ParseArgStrings--"3. 解析所有参数"-->OptTable::ParseArgs
OptTable::ParseArgs--3.1-->创建InputArgList
OptTable::ParseArgs--3.2-->别名2(while 语句遍历)--3.3 依次解析每个参数-->OptTable::ParseOneArg--3.4 创建 Option-->Option初始化方法
别名2(while 语句遍历)--3.8 更新 InputArgList-->ArgList::append


OptTable::ParseOneArg--"3.5 尝试解析单个参数 "-->Option::accept--"3.6 根据 Option 类型,分别创建 Arg"-->Option::acceptInternal-->别名3("创建 Arg")
Option::accept-->别名4("3.7 判断是否存在别名,并处理")

Driver::ParseArgStrings--4.1 --> 别名5["遇到*不支持*的参数时,抛出错误"]
Driver::ParseArgStrings--4.2 --> 别名6["遇到*不认识*的参数时,抛出错误"]


一、DriverOptTable

DriverOptTable 记录了 clang driver 相关的 Info 信息,是 OptTable 的子类

  1. DriverOptions 模块提供了函数 const llvm::opt::OptTable &clang::driver::getDriverOptTable() 可以获取 clang driver 支持的所有参数信息

    DriverOptTable 初始化时依赖的 InfoTable 参数是通过 clang/Driver/Options.inc 生成的

    通过下图,我们可以看到 InfoTable 的长度是 2776

    image

    小知识:当我们编译 llvm 项目时,会由 TableGen 工具将 Options.td 文件生成 Options.inc

    原始的文本信息如下:

    image

  2. 因为 DriverOptTable 继承自 OptTable,所以,这里会触发 OptTable 的初始化方法

    1. OptTable 的初始化时,会记录一些关键的 ID,用于后续使用,比如 TheInputOptionID
    2. 同时,会通过 PrefixChars 和 PrefixesUnion 记录合法的参数前缀,用于后续的快速参数合法性判断,比如 -v 参数的前缀是 -

    image

    image

二、Driver::ParseArgStrings

Driver::ParseArgStrings 方法的作用是将字符串数组解析为 ArgList,并做相关的校验

具体流程如下:

  1. 调用 Driver::getOpts 获取 clang driver 支持的所有参数 Info

  2. 调用 ParseArgs 解析命令行参数

  3. 对解析到的命令行参数进行判断,检测到 不支持 或者 未知 的参数时,会抛出异常

    image

如何区分 不支持 或者 不认识 的参数

  1. clang driver 不支持 的参数,都可以通过 Options.td 文件查到

    以 -pass-exit-codes 为例,gcc 支持该参数,但是 clang 不支持 此参数

    image

    image

  2. 不认识 的参数就是类似于 -test 这种,开发者随意拼写的参数

    image

三、ParseArgs

OptTable::ParseArgs 方法负责将字符串数组解析为 ArgList

具体流程如下:

  1. 先初始化 InputArgList 的实例,并存储原始的入参信息
  2. 通过 while 对原始参数字符串进行遍历,并通过 OptTable::ParseOneArg 方法将所有的原始参数字符串解析为 Arg 的实例
  3. 最后 Args 会持有所有的解析后的参数

通过添加调试代码,我们可以感受一下以下命令行对应的原始参数和解析后的 Arg 实例分别是什么样子

1
clang -arch arm64 /var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/C/main.m -target arm64-apple-ios11.1

image

四、ParseOneArg

OptTable::ParseOneArg 方法负责解析单个参数

具体流程如下:

  1. 先移除参数的前缀,并通过 std::lower_bound 查找第一个前缀匹配的 Info

    比如,-arch 会变成 arch

  2. 根据 Info 初始化 Option 持有参数信息

  3. 通过 Option::accept 方法校验参数是否正常

  4. 参数正常时直接返回

  5. 如果没有找到合适的参数,再判断参数是否以 / 开头,如果开始,会把参数当做源码文件进行处理

  6. 其它情况下,会当做参数当做 未知参数 进行下一步处理

image

  1. std::lower_bound 会依赖下面两个方法查找第一个前缀匹配的参数

    Info.Name 和 Name 的查找逻辑比较复杂,需要深入研究的同学,可以逐步调试帮助理解

    image

    image

  2. Option::accept 方法会依次进行以下处理

    1. 先转发到 Option::acceptInternal 方法进行参数校验
    2. 判断解析到的参数是否属于别名
    3. 如果别名,会进行特殊处理

    比如,-fembed-bitcode-marker 就是 -fembed-bitcode=marker 参数的别名,两个参数的意义完全相同

    image

  3. Option::acceptInternal 方法会根据 Option 的类型进行处理并生成 Arg 实例。

    因为 -arch 的类型是 SeparateClass ,所以,会将下一个原始参数字符串(arm64)当做 value 进行处理

    类型 示例
    Separate -arch arm64
    Flag -v
    Joined -fembed-bitcode=marker

    image

总结

本文通过分析 DriverOptTable 的生成机制并分析Driver::ParseArgStrings 内部流程,对 clang driver 的参数解析流程做了简单的分析

相关推荐

  • clang 源码导读(1): clang 导读
  • clang 源码导读(2): clang driver 流程简介
  • clang 源码导读(4): clang driver 构建 Actions
  • clang 源码导读(5): clang driver 构建 Jobs
  • clang 源码导读(6): clang driver 执行命令
#clang# #clang driver#
clang 源码导读(2): clang driver 流程简介
clang 源码导读(4): clang driver 构建 Actions
  • 文章目录
  • 站点概览
酷酷的哀殿

酷酷的哀殿

单身狗

74 日志
83 标签
    • 前言
    • 一、DriverOptTable
    • 二、Driver::ParseArgStrings
      • 如何区分 不支持 或者 不认识 的参数
    • 三、ParseArgs
    • 四、ParseOneArg
    • 总结
© 2021 酷酷的哀殿 京ICP备18052541号-2
Powered by - Hugo v0.80.0
Theme by - NexT
0%