rustc编译原理
rustc编译过程分析
rust的编译过程是个自举的过程,其中主要流程分为三个阶段,见上图。
从bootstrap
文档中大致能了解各个阶段的过程,但是具体代码功能对应关系还是建议通过在编译过程中的调试打印信息来了解整个过程
调试建议
具体分析细节建议通过调试手段进行
1.打印输出
在rust工程中编译命令中增加”-vv”可以输出cargo 和bootstarp中对应的verbose调试信息
也可以在工程配置文件config.toml文件中增加[build] verbose = 2 配置开关,说明具体可阅读config.toml.example中的内容
2.打开debug调试信息,通过rust-gdb或rust-lldb进行调试,具体打开方式配置根目录下的Cargo.toml,修改如下
这样做目的是在编译中增加完整的调试信息,方便调试器进行调试
经验:rust-lldb我再使用的时候发现目前lldb还不支持child子进程的fork调试,如果要跟踪调试子进程还是建议使用rust-gdb
各阶段的说明
rustc编译的入口脚本x.py
我这边使用的命令如下
python3 x.py build --stage 2 --target=aarch64-unknown-linux-gnu
编译stage 2的stage2的rustc和aarch64 对应的rust-std
其实官方的这张图基本上已经把各个阶段的编译流程说到了,主要问题是我们对bootstrap, sysroot的概念,以及生成的目录没有直观的认识,所以导致并不能很好理解图上的框和工程对应关系
那我们就结合日志去了解这个的实际过程。日志可以见附件
我这边对bootstrap工程内的rustc进行了修改,主要是放开了打印,因为直接用-vv的话编译的warning输出导致的干扰太大。
阶段0
- 首先是要有初始版本的rustc,这个版本主要用于作为stage0的编译器,
从日志上可以看到,编译工程首先是下载了beta版本的压缩包,这个版本是由rust/src/stage0.json里面的信息决定的
是由x.py的python代码里面实现的版本下载 - 接下来就是用Beta版本的rustc 编译bootstrap, 这个bootstrap是用rust语言实现的编译构建框架,开发文档的叫法也叫做RustBuild,主要功能是实现整个rustc的自举的编译过程。
二进制源码入口是/rust/src/bootstrap/bin/main.rs,其中比较重要的另外一个工具是/rust/src/bootstrap/bin/rustc.rs,开始会被这个名字误导,这个bootstrap下的rustc并不是rust编译器本身,实际是个对调用实际rustc编译命令的一个包装(wrapper),他包裹了调用rustc、cargo等工具,并在调用前准备好了env环境变量。 - 当编译完成bootstrap,我这边叫它构建系统(rustbuild),x.py将控制权交给了bootstrap,由bootstrap触发用Beta版本的rustc编译 rust-std标准库源码
编译器为Beta版本
输出路径为
这里要说明的是由于beta版本的编译器对一些nightly的特性没有默认支持,所以在stage0的编译命令中增加了–cfg=bootstrap参数,用以打开对unstable特性的支持,这个在dev开发文档中也有说明 - 编译完成 rust-std标准库之后会将标准库拷贝到stage0-sysroot中作为编译stage1-rustc的输入
可以看到std编译完成后,从stage0-std/x86_64-unknown-linux-gnu/release/deps 拷贝到了 stage0-sysroot/lib/rustlib/x86_64-unknown-linux-gnu/ 中 - 接下来就是编译stage1阶段使用的rustc编译器
期间会先源码构建llvm,主要是用llvm的codegen后端生成后端代码,目前rustc和llvm的gen怎么互相调用还不了解,待后续分析rustc进行展开
可以从日志看到最终Beta版本rustc编译生成的是rust_main,
以下将rust_main拷贝到了stage1/bin下rustc
阶段2
阶段1的流程相对和阶段0是类似的,只是这次使用的编译器为阶段1的输出的rustc(以下叫stage1-rustc)
- 使用stage1-rustc与stage0中生成的std,重新编译生成stage1的std
生成完成的生成件拷贝到x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib/路径下 - 使用stage1-rustc与刚生成的std,编译rustc相关的生成件
最终生成rust_main,并拷贝到了x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib/
3.拷贝生成的rust_main和库到stage2目录,作为stage2的输入编译器
阶段3
1.使用阶段2的编译器stage2-rustc 交叉编译aarch64-unknown-linux-gnu的标准库
2.最终生成的标准库拷贝到x86_64-unknown-linux-gnu/stage2/lib/rustlib/aarch64-unknown-linux-gnu/lib/下
- 进行rustdoc的编译
可以看到rustdoc使用的是用stage1-rustc编译产生的