推奨ワークフロー
完全なブートストラップ処理にはかなり時間がかかります。 作業を楽にするための提案をいくつか示します。
pre-push フックのインストール
CI は、コード品質を確保するための内部ツールである tidy に合格しない場合、ビルドを自動的に失敗させます。
必要であれば、各 push 時に ./x test tidy を自動的に実行し、コードが基準を満たしていることを確認する Git
hook をインストールできます。
フックが失敗した場合は、./x test tidy --bless を実行して変更をコミットしてください。
後で pre-push の動作が望ましくないと判断した場合は、.git/hooks 内の pre-push ファイルを削除できます。
ビルド済みの git フックは src/etc/pre-push.sh にあります。
これを .git/hooks フォルダーに pre-push としてコピーできます(.sh 拡張子は付けません!)。
./x setup の実行手順の一部としてフックをインストールすることもできます!
設定拡張
異なるタスクに取り組む場合、異なるブートストラップ設定を切り替える必要があるかもしれません。 将来の使用に備えて古い設定を保持しておきたい場合もあります。 しかし、生の設定値を ランダムなファイルに保存して手動でコピー&ペーストすると、特にさまざまな設定の長い履歴がある場合、すぐに煩雑になります。
複数の設定の管理を簡単にするために、設定拡張を作成できます。
たとえば、cross.toml という名前の単純な設定ファイルを作成できます。
[build]
build = "x86_64-unknown-linux-gnu"
host = ["i686-unknown-linux-gnu"]
target = ["i686-unknown-linux-gnu"]
[llvm]
download-ci-llvm = false
[target.x86_64-unknown-linux-gnu]
llvm-config = "/path/to/llvm-19/bin/llvm-config"
次に、これを bootstrap.toml に含めます。
include = ["cross.toml"]
拡張の中に拡張を再帰的に含めることもできます。
注: include フィールドでは、上書きロジックは右から左の順序に従います。
たとえば、
include = ["a.toml", "b.toml"] では、拡張 b.toml が a.toml を上書きします。
また、親の拡張は常に内側の拡張を上書きします。
rust-analyzer を rustc 用に設定する
“library” ツリーのチェック
“library” ツリーのチェックには stage1 コンパイラが必要であり、コンピューターによっては重い処理になる場合があります。
このため、ブートストラップには --skip-std-check-if-no-download-rustc というフラグがあり、rust.download-rustc が利用できない場合は
“library” ツリーのチェックをスキップします。
rust-analyzer によってコンピューターに重い負荷をかけるのを避けたい場合は、rust-analyzer 設定の
./x check コマンドに --skip-std-check-if-no-download-rustc フラグを追加できます。
プロジェクトローカルの rust-analyzer セットアップ
rust-analyzer は、ファイルを保存するたびにコードのチェックとフォーマットを行うのに役立ちます。
デフォルトでは、rust-analyzer は cargo check コマンドと rustfmt コマンドを実行しますが、
rustc に取り組む場合、これらのツールのより適したバージョンを使用するように、これらのコマンドを上書きできます。
カスタムセットアップにより、rust-analyzer はソースのチェックに ./x check を使用し、
フォーマットには stage 0 の rustfmt を使用できます。
デフォルトの rust-analyzer.check.overrideCommand コマンドラインは、リポジトリ内のすべての
クレートとツールをチェックします。
特定の部分に取り組んでいる場合は、
チェック時間を節約するために、作業中の部分だけをチェックするようにコマンドを上書きできます。
たとえば、コンパイラに取り組んでいる場合は、
コンパイラ部分だけをチェックするためにコマンドを x check compiler --json-output に上書きできます。
利用可能な部分を確認するには、x check --help --verbose を実行できます。
./x setup editor を実行すると、サポートされているエディターのいずれかについて、プロジェクトローカルの LSP 設定
ファイルを作成するよう求められます。
./x setup の実行手順の一部として設定ファイルを作成することもできます。
rust-analyzer に別のビルドディレクトリを使用する
デフォルトでは、rust-analyzer がチェックまたはフォーマットコマンドを実行する場合、手動のコマンドラインビルドと 同じビルドディレクトリを共有します。 これは、次の 2 つの理由で不便な場合があります。
- 各ビルドはビルドディレクトリをロックし、もう一方を待機させるため、rust-analyzer がバックグラウンドで コマンドを実行している間は、コマンドラインビルドを実行できなくなります。
- コンパイラフラグやその他の設定の競合により、いずれかのビルドが以前にビルドされた アーティファクトを削除してしまうリスクが高まり、場合によっては追加の再ビルドが必要になります。
これらの問題を回避するには、次のようにします。
- エディターの rust-analyzer 設定内のすべてのカスタム
xコマンドに--build-dir=build-rust-analyzerを追加します。 (必要に応じて別のディレクトリ名を選んでもかまいません。) rust-analyzer.rustfmt.overrideCommand設定を変更して、別のビルドディレクトリ内のrustfmtのコピーを指すようにします。rust-analyzer.procMacro.server設定を変更して、別のビルドディレクトリ内のrust-analyzer-proc-macro-srvのコピーを指すようにします。
コマンドラインビルドと rust-analyzer に別々のビルドディレクトリを使用するには、 追加のディスク容量が必要です。
Visual Studio Code
./x setup editor で vscode を選択すると、Visual Studio code を設定する
.vscode/settings.json ファイルを作成するよう求められます。
推奨される rust-analyzer 設定は src/etc/rust_analyzer_settings.json にあります。
保存時に ./x check を実行するのが不便な場合、VS Code では代わりに Build Task を使用できます。
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "./x check",
"command": "./x check",
"type": "shell",
"problemMatcher": "$rustc",
"presentation": { "clear": true },
"group": { "kind": "build", "isDefault": true }
}
]
}
Neovim
Neovim ユーザーには、いくつかの選択肢があります。
- 最も簡単な方法は neoconf.nvim を使用することですが、これは
非推奨の
require('lspconfig')API を使用しており、neovim 0.11+ で警告が表示されます。 coc.nvimを使用することも別の選択肢ですが、node.js がインストールされている必要があります。- rust-analyzer 設定を読み込むためのカスタムスクリプトを使用します。
neoconf.nvim
neoconf.nvim により、ネイティブ LSP でプロジェクトローカルの設定 ファイルを使用できます。 使用方法の手順は以下のとおりです。 これらは rust-analyzer がすでに Neovim で設定されていることを必要とする点に注意してください。 その手順は こちら にあります。
- まずプラグインをインストールします。 これは README の手順に従って行えます。
./x setup editorを実行し、vscodeを選択して.vscode/settings.jsonファイルを作成します。 このファイルが検出されると、プロジェクトが開かれたときにneoconfは rust-analyzer 設定を自動的に読み取り、更新できます。
coc.nvim
coc.nvim を使用している場合は、./x setup editor を実行して vim を選択し、
.vim/coc-settings.json を作成できます。
設定は :CocLocalConfig で編集できます。
推奨設定は src/etc/rust_analyzer_settings.json にあります。
カスタム LSP 設定
neovim 0.11+ を実行している場合は、 nvim-lspconfig とカスタムスクリプトだけで rust-analyzer を設定できます。
- rust-analyzer LSP がセットアップされていることを確認します
- 次の内容で
$HOME/.config/nvim/after/plugged/rust_analyzer.luaを作成します。
```lua
-- 上書きする前に、nvim-lspconfig/lsp/rust_analyzer.lua からデフォルト関数を取得します。
-- このファイルは after/plugin にあるため、nvim-lspconfig がすでに初期化済みであることが保証されます。
local default_root_dir = vim.lsp.config["rust_analyzer"].root_dir
local default_before_init = vim.lsp.config["rust_analyzer"].before_init
vim.lsp.config("rust_analyzer", {
cmd = { "rust-analyzer" },
filetypes = { "rust" },
-- rust-lang/rust をサポートするには、rust リポジトリ内にいることを検出し、cargo プロジェクトのルートではなく
-- git ルートを使用する必要があります。
root_dir = function(bufnr, on_dir)
local git_root = vim.fs.root(bufnr, { ".git" })
if git_root then
if vim.uv.fs_stat(vim.fs.joinpath(git_root, "src/etc/rust_analyzer_zed.json")) then
on_dir(git_root)
return
end
end
-- rust-lang/rust に一致しないものについては、デフォルトの root_dir にフォールバックします
default_root_dir(bufnr, on_dir)
end,
before_init = function(init_params, config)
-- rust-lang/rust 内にいる場合は、特別な rust-analyzer 設定を使用する必要があります。
local settings = vim.fs.joinpath(config.root_dir, "src/etc/rust_analyzer_zed.json")
if vim.uv.fs_stat(settings) then
local file = io.open(settings)
-- nvim 0.12+ はコメントをサポートしています。それ以外の場合は content:gsub("//[^\n]*", "") が必要です。
local json = vim.json.decode(file:read("*a"), { skip_comments = true })
file:close()
config.settings["rust-analyzer"] = vim.tbl_deep_extend(
"force", -- 競合がある場合は特別な設定で上書きします。
config.settings["rust-analyzer"] or {},
json.lsp["rust-analyzer"].initialization_options
)
end
default_before_init(init_params, config)
end,
})
vim.lsp.enable("rust_analyzer")
上述のビルドタスクを使用したい場合は、自分の設定内で独自のコマンドを作成するか、
overseer.nvim のようなプラグインをインストールできます。
これは VSCode の task.json ファイルを読み取る
ことができ、上記と同じ手順に従えます。
Emacs
Emacs は、Eglot を通じて
プロジェクトローカル設定による rust-analyzer のサポートを提供します。
Eglot を rust-analyzer とともにセットアップする手順は
こちらにあります。
Rust 開発全般向けに Emacs と Eglot のセットアップが済んだら、
./x setup editor を実行して emacs を選択できます。これにより、
Eglot 向けの推奨設定を含む .dir-locals.el を作成するよう促されます。
推奨設定は src/etc/rust_analyzer_eglot.el にあります。
プロジェクト固有の Eglot 設定について詳しくは、
マニュアルを参照してください。
Helix
Helix には LSP と rust-analyzer のサポートが組み込まれています。
こちらで説明されているように、
languages.toml を通じて設定できます。
./x setup editor を実行して helix を選択できます。これにより、
Helix 向けの推奨設定を含む languages.toml を作成するよう促されます。
推奨設定は src/etc/rust_analyzer_helix.toml にあります。
Zed
Zed には LSP と rust-analyzer のサポートが組み込まれています。
こちらで説明されているように、
.zed/settings.json を通じて設定できます。
./x setup editor で zed を選択すると、推奨設定で Zed を設定する
.zed/settings.json ファイルを作成するよう促されます。
推奨される rust-analyzer 設定は src/etc/rust_analyzer_zed.json にあります。
確認、確認、そして再確認
単純なリファクタリングを行う場合、./x check を継続的に実行すると便利です。
上述のように rust-analyzer をセットアップしていれば、これは
ファイルを保存するたびに自動的に行われます。
ここでは、コンパイラがビルドできることを確認しているだけですが、
多くの場合、それだけで十分です(たとえば、メソッド名を変更する場合)。
実際にテストを実行する必要があるときに ./x build を実行できます。
実際には、コードが動作するか 100% 確信できない場合でも、テストを後回しにすると便利なことがあります。
その場合、リファクタリングのコミットを積み重ね続け、後のある時点でテストだけを実行できます。
その後、git bisect を使って、どのコミットが問題を引き起こしたのかを正確に突き止められます。
このスタイルのうれしい副作用は、
最後にはかなり細かい単位のコミットの集合が残り、そのすべてが
ビルドでき、テストに合格していることです。
これはレビューに役立つことが多いです。
rustup が nightly を使用するように設定する
ブートストラッププロセスの一部では、rustfmt のようなツールの固定された nightly バージョンを使用します。
リポジトリ内で cargo fmt のようなものが正しく動作するようにするには、
rustup で nightly ツールチェーンをインストール し、次のコマンドを実行します。
cd <path to rustc repo>
rustup override set nightly
これを worktree をセットアップした すべてのディレクトリに対して行うことを忘れないでください。
src/stage0 の固定された nightly バージョンを使用する必要がある場合もありますが、
多くの場合は通常の nightly チャネルで動作します。
注記 実際に x が使用する rustfmt で vscode を設定する方法については
VSCode に関するセクションを、ブートストラップされたコンパイラ用に
rustup ツールチェーンをセットアップする方法については rustup に関するセクションを参照してください
注記 これで rustc を cargo で直接ビルドできるようになるわけでは_ありません_。
コンパイラまたは標準ライブラリで作業するには、引き続き x を使用する必要があります。これは単に
cargo fmt を使えるようにするだけです。
CI-rustc によるより高速なビルド
コンパイラに取り組んでいない場合、多くの場合はコンパイラツリーをビルドする必要はありません。
たとえば、コンパイラのビルドをスキップし、library ツリーまたは
src/tools 配下のツールだけをビルドできます。
これを実現するには、設定で download-rustc
オプションを設定して有効にする必要があります。
これにより bootstrap は、stage > 0 の
ステップに対して最新の nightly コンパイラを使用します。つまり、事前コンパイル済みコンパイラが 2 つ存在することになります:
stage0 コンパイラと、stage > 0 のステップ用の download-rustc コンパイラです。
この方法では、ツリー内コンパイラをビルドする必要がまったくなくなります。
その結果、ツリー内コンパイラをビルドしないことで、ビルド時間が大幅に短縮されます。
--keep-stage-std によるより高速な再ビルド
コンパイラがビルドできるかどうかを確認するだけでは不十分な場合があります。
よくある例として、何らかの状態の値を調べたり、問題をよりよく理解したりするために
debug! 文を追加する必要がある場合があります。
その場合、実際には完全なビルドは必要ありません。
bootstrap のキャッシュ無効化をバイパスすることで、多くの場合、
これらのビルドを非常に高速に完了できます(たとえば約 30 秒)。唯一の注意点は、
これには多少のごまかしが必要であり、動作しないコンパイラが生成される可能性があることです(ただし、
それは簡単に検出して修正できます)。
使用したいコマンドの流れは次のとおりです。
- 初回ビルド:
./x build library - 2 回目以降のビルド:
./x build library --keep-stage-std=1- ここでは
--keep-stage-std=1フラグを追加していることに注意してください
- ここでは
前述のとおり、--keep-stage-std=1 の効果は、古い標準ライブラリを
再利用できると単に_仮定_することです。
コンパイラを編集している場合、これは
多くの場合正しいです。結局、標準ライブラリは変更していないからです。
しかし、正しくない場合もあります。たとえば、型やその他の状態を
rlib ファイルへコンパイラがどのようにエンコードするかを制御する、
コンパイラの「メタデータ」部分を編集している場合や、メタデータに含まれるもの
(MIR の定義など)を編集している場合です。
要約すると、--keep-stage-std=1 を使用していると、コンパイル時に奇妙な挙動が
発生する可能性があります – たとえば、奇妙な ICE
やその他の panic です。
その場合は、コマンドから --keep-stage-std=1 を単に削除して再ビルドしてください。
それで問題は解決するはずです。
テストを実行するときにも --keep-stage-std=1 を使用できます。
次のようになります。
- 初回テスト実行:
./x test tests/ui - 2 回目以降のテスト実行:
./x test tests/ui --keep-stage-std=1
インクリメンタルコンパイルの使用
さらに --incremental フラグを有効にして、2 回目以降の再ビルドでさらに時間を節約できます。
./x test tests/ui --incremental --test-args issue-1234
毎回のコマンドにこのフラグを含めたくない場合は、bootstrap.toml で有効にできます。
[rust]
incremental = true
インクリメンタルコンパイルは通常より多くのディスク容量を使用することに注意してください。
ディスク容量が気になる場合は、build
ディレクトリのサイズをときどき確認するとよいでしょう。
最適化の微調整
optimize = false を設定すると、コンパイラがテストには遅すぎるようになります。
ただし、テストサイクルを改善するために、再ビルドが必要な
crate に対してのみ最適化を選択的に無効化できます
(source)。
たとえば、rustc_mir_build に取り組んでいる場合、rustc_mir_build と
rustc_driver の crate はインクリメンタル再ビルドに最も時間がかかります。
そのため、ルートの Cargo.toml に次のように設定できます。
[profile.release.package.rustc_mir_build]
opt-level = 0
[profile.release.package.rustc_driver]
opt-level = 0
複数のブランチで同時に作業する
複数のブランチで並行して作業するのは少し面倒な場合があります。というのも、 あるブランチでコンパイラをビルドすると、古いビルドとインクリメンタル コンパイルキャッシュが上書きされるからです。 1 つの解決策は、リポジトリの clone を複数用意することですが、 それは Git メタデータを複数回保存することを意味し、 各 clone を個別に更新しなければなりません。
幸い、Git には worktrees と呼ばれるよりよい解決策があります。
これにより、同じ Git データベースを共有する複数の「working tree」を作成できます。
さらに、
すべての worktree が同じオブジェクトデータベースを共有するため、いずれかで
ブランチ(例: main)を更新すれば、どの
worktree からでも新しい commit を使用できます。
ただし、1 つ注意点として、submodule は共有されません。
それらは依然として複数回 clone されます。
Rust リポジトリのルートディレクトリ内にいるとして、次のコマンドを実行することで、 新しい “rust2” ディレクトリに「リンクされた working tree」を作成できます。
git worktree add ../rust2
main を基にした新しいブランチ用に新しい worktree を作成する場合は、次のようになります。
git worktree add -b my-feature ../rust2 main
その後、その rust2 フォルダーを rustc の変更とビルドのための独立したワークスペースとして使用できます!
nix での作業
いくつかの nix 設定が src/tools/nix-dev-shell に定義されています。
direnv を使用している場合は、src/tools/nix-dev-shell/envrc-flake または src/tools/nix-dev-shell/envrc-shell へのシンボリックリンクを作成できます
ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc # flake を使用
または
ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc # nix-shell を使用
flake を使用している場合は、次のコマンドでそれも更新するようにしてください。
nix flake update --flake ./src/tools/nix-dev-shell
この shell は、すべての依存関係が正しく設定された状態で ./x.py スクリプトを実行する
x という名前のコマンドを作成します。
注意
NixOS ではないディストリビューションで nix を使用する場合、
bootstrap.toml で build.patch-binaries-for-nix = true を設定する必要がある場合があることに注意してください。Bootstrap は
nix 内で実行されているかどうかを検出し、自動的にパッチ適用を有効にしようとしますが、この
検出では false negative が発生する可能性があります。
nix shell を使用して bootstrap.toml を管理することもできます。
let
config = pkgs.writeText "rustc-config" ''
# bootstrap.toml の内容をここに記述します
''
pkgs.mkShell {
/* ... */
# この環境変数は bootstrap に bootstrap.toml の場所を伝えます。
RUST_BOOTSTRAP_CONFIG = config;
}
Shell 補完
Bash、Zsh、Fish、PowerShell を使用している場合、x.py 用に自動生成された shell
補完スクリプトは
src/etc/completions にあります。
source ./src/etc/completions/x.py.<extension> を使用して、選択した shell 用の補完を
読み込むことができます。または、PowerShell の場合は & .\src\etc\completions\x.py.ps1 を使用できます。
これを shell の起動スクリプト(例: .bashrc)に追加すると、この補完が自動的に
読み込まれます。