Rustコンパイラのコンパイルの流れ

Rustコンパイラは同梱のrustbuildというツールでビルドされる。これはRustとPython2で書かれている。 README.md にも説明が書かれているが、ここで改めて説明をしてみる。

./x.pysrc/bootstrap/bootstrap.py にリンクされている。これは次のような動作をする。

  • 設定ファイル (config.mk または config.toml) を読む。
    • bootstrap.py はTOMLパーサーを持たないため、この時点では config.tomlアドホックな方法で解析される。したがって、 vendor, locked-deps, cargo, rustc, build キーの記述には気をつける必要がある。例えば cargo = ".."cargo=".." と書くと認識されない。
  • 必要なら、 src/stage0.txt を読み、インターネットからstage0コンパイラ(ビルド済みのRustコンパイラ)を取得する。
  • 必要なら、rustbuild自身をビルドする。 cargo build --manifest-path src/bootstrap/Cargo.toml が実行される。
  • rustbuildを呼び出す。 build/bootstrap/debug/bootstrap "$@" が実行される。

rustbuildの本体はRustで書かれている。特に重要なのが step.rs である。ここにMakefileのような依存関係が記述されている。

./x.py build をしたときのrustbuildの手順は大雑把にいうと次の通りである。

  • stage0コンパイラを用いて、stage1コンパイラをビルドする。(stage0標準ライブラリにリンクされる)
  • stage1コンパイラを用いて、stage1標準ライブラリをビルドする。
  • stage1コンパイラを用いて、stage2コンパイラをビルドする。(stage1標準ライブラリにリンクされる)
  • stage1標準ライブラリはそのままstage2標準ライブラリとして用いられる。

このように2回コンパイルが必要なのは、Rustがバージョン間でABI互換性を保たないことに由来する。

ここでstage0コンパイラのバージョンをV0とし、現在作ろうとしているコンパイラのバージョンをV1とする。すると、各ステージのコンパイラと標準ライブラリのバージョンは以下のようになる。

stage0やstage2のように、自身と互換なABIでコンパイルされているコンパイラをfull host compilerという。

full host compilerが必要なのは、しばしばコンパイラプラグインのビルドが必要なためである。コンパイラプラグインコンパイラ自身にリンクされるプログラムだから、stage1コンパイラコンパイラプラグインを正しく扱うことができない。

なお、Rustで最もよく使われるコンパイラプラグインの形式はおそらく手続きマクロ(proc-macro)である。

まとめ

  • RustコンパイラはRustで書かれている。最初のRustはstage0といい、既にあるバイナリをダウンロードする。
  • Rustコンパイラはデフォルトで2回ビルドされる。こうしないと手続きマクロが動かない。