Rustコンパイラのコンパイルの流れ
Rustコンパイラは同梱のrustbuildというツールでビルドされる。これはRustとPython2で書かれている。 README.md
にも説明が書かれているが、ここで改めて説明をしてみる。
./x.py
は src/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コンパイラ: V0 ABIでコンパイルされているV0のコンパイラ
- stage0標準ライブラリ: V0 ABIでコンパイルされているライブラリ
- stage1コンパイラ: V0 ABIでコンパイルされているV1のコンパイラ
- stage1標準ライブラリ: V1 ABIでコンパイルされているライブラリ (=stage2標準ライブラリ)
- stage2コンパイラ: V1 ABIでコンパイルされているV1のコンパイラ
stage0やstage2のように、自身と互換なABIでコンパイルされているコンパイラをfull host compilerという。
full host compilerが必要なのは、しばしばコンパイラプラグインのビルドが必要なためである。コンパイラプラグインはコンパイラ自身にリンクされるプログラムだから、stage1コンパイラはコンパイラプラグインを正しく扱うことができない。
なお、Rustで最もよく使われるコンパイラプラグインの形式はおそらく手続きマクロ(proc-macro)である。