编译器源代码概览

注意:代码仓库的结构正在经历许多转变。特别是,我们希望最终顶层目录下具有编译器、构建系统、标准库等的单独目录,而不是一个庞大的 src/ 目录。 自2021年 1月起,标准库已移至 library/,构成 rustc 编译器本身的 crate 已移至 compiler/

现在,我们已经大体了解了编译器的工作,让我们看一下 rust-lang/rust 仓库内容的结构。

Workspace 结构

rust-lang/rust 存储库由一个大型cargo workspace组成,该 Workspace 包含编译器,标准库(coreallocstdproc_macro等)和 rustdoc,以及构建系统以及用于构建完整 Rust 发行版的一些工具和子模块。 在撰写本文时,此结构正在逐步进行一些转换,以使其变得不再是一个巨大的代码仓库且更易于理解,尤其是对于新手。 该存储库由三个主要目录组成:

  • compiler/ 包含了 rustc 的源代码。它包含了组成编译器的一系列crate。
  • library/ 包含标准库 (coreallocstdproc_macrotest)以及 Rust 运行时(backtracertstartuplang_start
  • src/ 包含 rustdocclippycargo、 构建系统、语言文档等等。

标准库

标准库 crate 都在 library/中。它们的名称都非常直观,如 stdcorealloc等。还有 proc_macrotest 和其他运行时库。这些代码和其他 Rust crate 非常相似,区别在于它们必须以特殊的方式构建,因为其中可以使用不稳定的功能。

编译器

建议先阅读概述章节,它概述了编译器的工作方式。

本节中提到的 crate 组成了整个编译器,它们位于 compiler/ 中。

compiler/ 下的 crate 们的名称均以rustc_ *开头。这里有大约 50 个或大或小,相互依赖的 crate。 还有一个 rustc crate,它是实际的二进制文件入口点(即 main 函数)所在之处; 除了调用rustc_drivercrate之外,rustc crate实际上并不做任何事情,rustc_driver crate 会驱动其他 crate 中的各个部分来进行编译。

这些 crate 之间的依赖关系很复杂,但大体来说:

您可以通过读取各个 crate 的 Cargo.toml 来查看确切的依赖关系,就像普通的Rust crate一样。

最后一件事:src/llvm-project 是指向我们自己的 LLVM fork的子模块。 在bootstrap过程中,将会构建LLVM, compiler/rustc_llvm 是LLVM(用C++编写)的 Rust 包装,以便编译器可以与其交互。 本书的大部分内容是关于 Rust 编译器的,因此在这里我们将不对这些 crate 做任何进一步的解释。

Big Picture

这种由多个 crate 互相依赖的代码结构受两个主要因素的强烈影响:

  1. 组织。编译器是一个 巨大的 代码库;将其放在一整个大 crate 中是不可能的。依赖关系结构部分反映了编译器的代码结构。
  2. 编译时间。通过将编译器分成多个 crate,我们可以更好地利用 cargo 进行增量/并行编译。特别是,我们尝试使板条箱之间的依赖关系尽可能少,这样,如果您更改一个 crate,我们就不必重新构建大量的 crate。

在依赖关系树的最底部是整个编译器使用的少数 crate(例如 rustc_span)。编译过程中的非常早期的部分(例如,parsing 和 AST)仅取决于这些。

构建AST之后不久,编译器的 查询系统 就建立好了。查询系统是使用函数指针以巧妙的方式设置的。这使我们可以打破 crate 之间的依赖关系,从而可以并行地进行更多编译。 但是,由于查询系统是在 rustc_middle 中定义的,编译器的几乎所有后续部分都依赖于此 crate。这是一个非常大的 crate,导致其编译时间极长。我们已经做出了一些努力来将内容从其中移出,但效果有限。另一个不幸的副作用是,有时相关功能分散在不同的 crate 中。例如,linting 功能分散在板条箱的较早部分 rustc_lintrustc_middle 和其他地方。

一般而言,在理想世界中,应当使用更少的,更内聚的板条箱,使用增量和并行编译确保编译时间保持合理。 但是,我们的增量和并行编译暂时还没有那么好用,所以目前为止我们的解决方案只能是东西分进单独的 crate。

在依赖树的顶部是 rustc_interfacerustc_driver板条箱。

rustc_interface 是一个不稳定的查询系统的包装,用于帮助推动编译的各个阶段。

其他编译器中的的消费者(例如 rustdoc 或者甚至是 rust-analyzer)可以以不同的方式使用此接口。

rustc_driver crate 首先解析命令行参数,然后使rustc_interface驱动编译完成。

rustdoc

rustdoc 的大部分位于 librustdoc 中。 但是,rustdoc二进制文件本身 src/tools/rustdoc,除了调用 rustdoc::main外,它什么都不做。 在 [src/tools/ rustdoc-js] 和 src/tools/rustdoc-themes 中,还有 rustdocs 的 javascript 和 CSS。 您可以在本章中阅读有关 rustdoc 的更多信息。

测试

以上所有内容的测试套件都在 src/test/ 中。 您可以在本章中了解有关测试套件的更多信息。 测试工具本身在 src/tools/compiletest 中。

构建系统

代码仓库中有许多工具,可用于构建编译器,标准库,rustdoc,以及进行测试,构建完整的 Rust 发行版等。 主要工具之一是 src/bootstrap。 您可以在这一章中了解有关 bootstrap的更多信息。 构建过程重还可能使用 src/tools/中的其他工具,例如 [tidy] 或 [compiletest]。

其他

rust-lang/rust 仓库中还有很多其他与构建完整 Rust 发行版有关的东西。 大多数时候,您无需关心它们。 这些包括:

  • [src/ci]:CI配置。 这里的代码实际上相当多,因为我们在许多平台上运行了许多测试。
  • [src/doc]:各种文档,包括指向几本书的submodule。
  • [src/etc]:其他实用程序。
  • [src/tools/rustc-workspace-hack],以及其他:各种变通方法以使 cargo 在bootstrapping 过程中运行。
  • 以及更多……