Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ライブラリとメタデータ

コンパイラが外部クレートへの参照を見つけると、そのクレートに関する何らかの 情報を読み込む必要があります。 この章では、そのプロセスの概要と、 クレートライブラリでサポートされているファイル形式について説明します。

ライブラリ

クレート依存関係は、rlib、dylib、または rmeta ファイルから読み込めます。 これらのファイル形式の重要な点は、rustc 固有の メタデータ を含んでいることです。 このメタデータにより、コンパイラは外部クレートに含まれるアイテム、 そのクレートがエクスポートするマクロ、そしてその他多くのことを理解するために十分な 情報を発見できます。

rlib

rlib は archive file であり、tar ファイルに似ています。 このファイル形式は rustc 固有であり、時間とともに変更される可能性があります。 このファイルには次のものが含まれます。

  • オブジェクトコード。これはコード生成の結果です。 これは通常のリンク時に使用されます。 各 codegen unit ごとに個別の .o ファイルがあります。 コード生成ステップは -C linker-plugin-lto CLI オプションでスキップでき、 これは各 .o ファイルに LLVM ビットコードのみが含まれることを意味します。
  • LLVM bitcode。これは LLVM の中間表現をバイナリで表したもので、 .o ファイル内のセクションとして埋め込まれます。 これは Link Time Optimization (LTO) に使用できます。 LTO が不要な場合は、コンパイル時間を改善し、ディスク使用量を削減するために、 -C embed-bitcode=no CLI オプションでこれを削除できます。
  • lib.rmeta という名前のファイル内の rustc metadata。
  • シンボルテーブル。これは本質的には、そのシンボルを含む オブジェクトファイルへのオフセットを持つシンボルの一覧です。 これはアーカイブファイルではごく標準的なものです。

dylib

dylib はプラットフォーム固有の共有ライブラリです。 これは .rustc と呼ばれる特別なリンクセクションに rustc metadata を含みます。

rmeta

rmeta ファイルは、クレートの metadata を含むカスタムバイナリ形式です。 このファイルは、すべてのコード生成をスキップすることでプロジェクトの高速な「チェック」 (cargo check で行われるようなもの)を行ったり、ドキュメント作成に十分な情報を収集したり (cargo doc で行われるようなもの)、あるいは pipelining のために使用できます。 このファイルは、--emit=metadata CLI オプションが使用された場合に作成されます。

rmeta ファイルはコンパイル済みのオブジェクトファイルを含まないため、リンクをサポートしません。

メタデータ

メタデータには、非常に幅広いさまざまな要素が含まれています。 このガイドでは、含まれるすべてのフィールドの詳細には立ち入りません。 含まれるさまざまな要素の感覚をつかむために、 CrateRoot の定義に目を通すことをお勧めします。 メタデータのエンコードとデコードに関するものはすべて rustc_metadata パッケージ内にあります。

含まれるもののいくつかのハイライトを次に示します。

  • rustc コンパイラのバージョン。 コンパイラは、他のどのバージョンのファイルも読み込むことを拒否します。
  • Strict Version Hash (SVH)。 これは正しい依存関係が読み込まれるようにするのに役立ちます。
  • Stable Crate Id。 これはクレートを識別するために使用されるハッシュです。
  • ライブラリ内のすべてのソースファイルに関する情報。 これは、依存関係内のソースを指す診断など、さまざまな用途に使用できます。
  • エクスポートされたマクロ、トレイト、型、アイテムに関する情報。 一般に、 パスがクレート依存関係内の何かを参照するときに知る必要があるあらゆるものです。
  • エンコードされた MIR。 これは任意であり、コード生成に必要な場合にのみエンコードされます。 cargo check はパフォーマンス上の理由からこれをスキップします。

Strict Version Hash

Strict Version Hash(SVH、別名「クレートハッシュ」)は、正しいクレート依存関係が 読み込まれるようにするために使用される 64 ビットハッシュです。 1 つのディレクトリに、異なる設定でビルドされた、または異なるソースからビルドされた 同じ依存関係の複数のコピーが含まれることがあります。 クレートローダーは、誤った SVH を持つクレートをすべてスキップします。

SVH は incremental compilation セッションのファイル名にも使用されますが、 その用途は主に歴史的なものです。

このハッシュにはさまざまな要素が含まれます。

  • HIR ノードのハッシュ。
  • すべての上流クレートのハッシュ。
  • すべてのソースファイル名。
  • 特定のコマンドラインフラグ(Stable Crate Id を介した -C metadata など)と、[TRACKED] とマークされたすべての CLI オプションのハッシュ。

ハッシュが実際に計算される場所については、compute_hir_hash を参照してください。

Stable Crate Id

StableCrateId は、同じ名前である可能性のある異なるクレートを識別するために使用される 64 ビットハッシュです。 これは、StableCrateId::new で計算されるクレート名とすべての -C metadata CLI オプションのハッシュです。 これは、シンボル名マングリング、クレートの読み込みなど、 さまざまな場所で使用されます。

デフォルトでは、すべての Rust シンボルはマングルされ、stable crate id が組み込まれます。 これにより、同じクレートの複数のバージョンを一緒に含めることができます。 Cargo は、パッケージバージョン、ソース、ターゲット種別など、さまざまな要因に基づいて -C metadata ハッシュを自動的に生成します(lib と test は同じ クレート名を持つことができるため、曖昧さを解消する必要があります)。

クレートの読み込み

クレートの読み込みには、かなりの数の微妙な複雑さが伴うことがあります。 name resolution 中に、外部クレートが(extern crate または パスを介して)参照されると、リゾルバは CStore を使用します。これは、 クレートライブラリを見つけ、それらの metadata を読み込む責任を負います。 依存関係が読み込まれた後、CStore はリゾルバがその処理を行うために必要な情報 (マクロの展開、パスの解決など)を提供します。

各外部クレートを読み込むために、CStore は CrateLocator を使用して、 特定の 1 つのクレートに対応する正しいファイルを実際に見つけます。 locator モジュールには、読み込みの仕組みを詳細に説明した優れたドキュメントがあり、 全体像を把握するために読むことを強くお勧めします。 依存関係の場所は、いくつかの異なる場所に由来する可能性があります。 直接依存関係は通常、--extern フラグで渡され、ローダーはそれらを直接参照できます。 直接依存関係には多くの場合、それ自身の依存関係への参照も含まれており、それらも読み込む必要があります。 これらは通常、-L フラグで渡されたディレクトリをスキャンし、メタデータに一致するクレート名と SVH が含まれるファイルを探すことで見つかります。 ローダーは依存関係を見つけるために sysroot も参照します。

クレートが読み込まれると、それらは CrateMetadata 構造体でラップされたクレートメタデータとともに CStore に保持されます。 解決と展開の後、CStore はコンパイルの残りの処理のために GlobalCtxt に渡されます。

パイプライン化

コンパイル時間を改善するための 1 つのテクニックは、依存関係のメタデータが利用可能になり次第、クレートのビルドを開始することです。 ライブラリの場合、依存関係のコード生成が完了するまで待つ必要はありません。 Cargo は、各依存関係について rlib だけでなく rmeta ファイルも出力するよう rustc に指示することで、この手法を実装しています。 rustc は可能な限り早い段階で、コード生成フェーズに進む前に rmeta ファイルをディスクに保存します。 コンパイラは JSON メッセージを送信して、可能であれば次のクレートのビルドを開始できることをビルドツールに知らせます。

クレート読み込みシステムは十分に賢く、rmeta ファイルを見つけたときに、rlib が存在しない場合(または部分的にしか書き込まれていない場合)にはそれを使用することを認識できます。

このパイプライン化はバイナリでは不可能です。なぜなら、リンクフェーズではすべての依存関係のコード生成が必要になるためです。 将来的には、リンクを別個のコマンドに分割することで、このシナリオをさらに改善できる可能性があります(#64191 を参照)。