Bazel のロックファイル機能を使用すると、プロジェクトに必要なソフトウェア ライブラリやパッケージの特定のバージョンや依存関係を記録できます。これは、モジュールの解決と拡張機能の評価の結果を保存することで実現されます。ロックファイルは再現可能なビルドを促進し、一貫性のある開発環境を確保します。また、プロジェクトの依存関係の変更の影響を受けない解決プロセスの部分を Bazel がスキップできるようにすることで、ビルド効率を高めることができます。さらに、ロックファイルは、外部ライブラリの予期しない更新や破壊的な変更を防ぐことで安定性を向上させ、バグの発生リスクを軽減します。
ロックファイルの生成
ロックファイルは、ワークスペースのルートの下に MODULE.bazel.lock
という名前で生成されます。これは、ビルドプロセス(特にモジュールの解決と拡張機能の評価後)で作成または更新されます。重要なのは、ビルドの現在の呼び出しに含まれている依存関係のみが含まれていることです。
プロジェクトで依存関係に影響する変更が行われると、ロックファイルが自動的に更新され、新しい状態が反映されます。これにより、ロックファイルは現在のビルドに必要な特定の依存関係セットに焦点を当て続け、プロジェクトの解決済み依存関係を正確に表すことができます。
ロックファイルの使用
ロックファイルはフラグ --lockfile_mode
で制御して、プロジェクトの状態がロックファイルと異なる場合の Bazel の動作をカスタマイズできます。使用可能なモードは次のとおりです。
update
(デフォルト): ロックファイルに存在する情報を使用して、既知のレジストリ ファイルのダウンロードをスキップし、結果が最新の拡張機能の再評価を回避します。情報が不足している場合は、ロックファイルに追加されます。このモードでは、変更されていない依存関係の変更情報(ヤンク バージョンなど)の更新も回避されます。refresh
:update
と同様ですが、変更可能な情報は、このモードに切り替えるときと、このモードにいる間、約 1 時間ごとに常に更新されます。error
:update
と同様ですが、情報が不足している場合や古い場合は、Bazel がエラーで失敗します。このモードでは、解決中にロックファイルの変更やネットワーク リクエストが実行されることはありません。reproducible
としてマークされたモジュール拡張機能は、ネットワーク リクエストを実行する可能性がありますが、常に同じ結果が生成されることが期待されます。off
: ロックファイルはチェックも更新もされません。
ロックファイルのメリット
ロックファイルには次のようなメリットがあり、さまざまな方法で使用できます。
再現可能なビルド。ロックファイルは、ソフトウェア ライブラリの特定のバージョンまたは依存関係をキャプチャすることで、さまざまな環境で、また時間の経過とともにビルドを再現できるようにします。デベロッパーは、プロジェクトの構築時に一貫した予測可能な結果を期待できます。
迅速な段階的な解決。ロックファイルを使用すると、Bazel は以前のビルドですでに使用されたレジストリ ファイルをダウンロードしなくて済みます。これにより、特に解決に時間がかかる場合のビルド効率が大幅に向上します。
安定性とリスクの軽減。ロックファイルは、外部ライブラリの予期しない更新や破壊的な変更を防ぐことで、安定性を維持するのに役立ちます。依存関係を特定のバージョンにロックすることで、互換性のない更新やテストされていない更新が原因でバグが発生するリスクが軽減されます。
ロックファイルの内容
ロックファイルには、プロジェクトの状態が変化したかどうかを判断するために必要なすべての情報が含まれています。また、現在の状態でプロジェクトをビルドした結果も含まれます。ロックファイルは主に 2 つの部分で構成されています。
- モジュール解決の入力となるすべてのリモート ファイルのハッシュ。
- ロックファイルには、モジュール拡張機能ごとに、それに影響する入力(
bzlTransitiveDigest
、usagesDigest
などのフィールドで表されます)と、その拡張機能の実行の出力(generatedRepoSpecs
と呼ばれます)が含まれます。
ロックファイルの構造と各セクションの説明を示す例を次に示します。
{
"lockFileVersion": 10,
"registryFileHashes": {
"https://bcr.bazel.build/bazel_registry.json": "8a28e4af...5d5b3497",
"https://bcr.bazel.build/modules/foo/1.0/MODULE.bazel": "7cd0312e...5c96ace2",
"https://bcr.bazel.build/modules/foo/2.0/MODULE.bazel": "70390338... 9fc57589",
"https://bcr.bazel.build/modules/foo/2.0/source.json": "7e3a9adf...170d94ad",
"https://registry.mycorp.com/modules/foo/1.0/MODULE.bazel": "not found",
...
},
"selectedYankedVersions": {
"foo@2.0": "Yanked for demo purposes"
},
"moduleExtensions": {
"//:extension.bzl%lockfile_ext": {
"general": {
"bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
"usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=",
...,
"generatedRepoSpecs": {
"hello": {
"bzlFile": "@@//:extension.bzl",
...
}
}
}
},
"//:extension.bzl%lockfile_ext2": {
"os:macos": {
"bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
"usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
...,
"generatedRepoSpecs": {
"hello": {
"bzlFile": "@@//:extension.bzl",
...
}
}
},
"os:linux": {
"bzlTransitiveDigest": "eWDzxG/aLsyY3Ubrto....+Jp4maQvEPxn0pLK=",
"usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
...,
"generatedRepoSpecs": {
"hello": {
"bzlFile": "@@//:extension.bzl",
...
}
}
}
}
}
}
レジストリ ファイル ハッシュ
registryFileHashes
セクションには、モジュール解決中にアクセスされたリモート レジストリのすべてのファイルのハッシュが含まれています。解決アルゴリズムは同じ入力が与えられた場合に完全に確定的であり、すべてのリモート入力がハッシュ化されるため、ロックファイル内のリモート情報の過度の重複を回避しながら、完全に再現可能な解決結果が得られます。なお、特定のレジストリには特定のモジュールが含まれていないものの、優先順位の低いレジストリには含まれていた場合についても記録する必要があります(例の「見つかりません」エントリを参照)。本質的に変更可能なこの情報は、bazel mod deps --lockfile_mode=refresh
を介して更新できます。
Bazel は、ロックファイルのハッシュを使用して、リポジトリ キャッシュ内のレジストリ ファイルを検索してからダウンロードします。これにより、後続の解決が高速化されます。
選択した yanked バージョン
selectedYankedVersions
セクションには、モジュール解決によって選択されたモジュールの yanked バージョンが含まれています。通常、これによりビルド時にエラーが発生するため、ヤンクされたバージョンが --allow_yanked_versions
または BZLMOD_ALLOW_YANKED_VERSIONS
を介して明示的に許可されている場合にのみ、このセクションは空になりません。
モジュール ファイルと比較すると、ヤンクされたバージョン情報は本質的に変更可能であり、ハッシュから参照できないため、このフィールドが必要になります。この情報は bazel mod deps --lockfile_mode=refresh
で更新できます。
モジュール拡張機能
moduleExtensions
セクションは、現在の呼び出しで使用されている拡張機能、または以前に呼び出された拡張機能のみを含むマップで、使用されなくなった拡張機能は除外されます。つまり、依存関係グラフ全体で拡張機能が使用されていない場合は、moduleExtensions
マップから削除されます。
拡張機能がオペレーティング システムやアーキテクチャのタイプに依存しない場合は、このセクションに「一般」のエントリが 1 つだけ表示されます。それ以外の場合は、OS、アーキテクチャ、またはその両方に基づいて名前が付けられた複数のエントリが含まれ、それぞれがそれぞれの仕様で拡張機能を評価した結果に対応します。
拡張機能マップの各エントリは、使用される拡張機能に対応しており、そのファイルと名前で識別されます。各エントリに対応する値には、その拡張機能に関連する情報が含まれています。
bzlTransitiveDigest
は、拡張機能の実装と、それによって間接的に読み込まれる .bzl ファイルのダイジェストです。usagesDigest
は、依存関係グラフ内の拡張機能の使用状況のダイジェストです。これにはすべてのタグが含まれます。- 拡張機能への他の入力(読み取るファイルやディレクトリの内容、使用する環境変数など)を追跡する、未指定のフィールド。
generatedRepoSpecs
は、拡張機能によって作成されたリポジトリを現在の入力でエンコードします。- オプションの
moduleExtensionMetadata
フィールドには、拡張機能によって提供されるメタデータが含まれます。たとえば、拡張機能によって作成された特定のリポジトリをルート モジュールがuse_repo
経由でインポートするかどうかなどです。この情報はbazel mod tidy
コマンドに使用されます。
モジュール拡張機能は、reproducible = True
を使用して返されるメタデータを設定することで、ロックファイルへの追加をオプトアウトできます。これにより、同じ入力が与えられた場合に常に同じリポジトリが作成されることが保証されます。
ベスト プラクティス
ロックファイル機能のメリットを最大限に活用するには、次のベスト プラクティスを検討してください。
プロジェクトの依存関係や構成の変更を反映するように、ロックファイルを定期的に更新します。これにより、以降のビルドが最新かつ正確な依存関係のセットに基づくようになります。すべての拡張機能を一度にロックダウンするには、
bazel mod deps --lockfile_mode=update
を実行します。ロックファイルをバージョン管理に含めてコラボレーションを促進し、すべてのチームメンバーが同じロックファイルにアクセスできるようにすることで、プロジェクト全体で一貫した開発環境を促進します。
bazelisk
を使用して Bazel を実行し、ロックファイルに対応する Bazel バージョンを指定する.bazelversion
ファイルをバージョン管理に含めます。Bazel 自体はビルドの依存関係であるため、ロックファイルは Bazel バージョンに固有であり、下位互換性のある Bazel リリース間でも変わります。bazelisk
を使用すると、すべてのデベロッパーがロックファイルに一致する Bazel バージョンを使用できます。
これらのベスト プラクティスに従うことで、Bazel のロックファイル機能を効果的に活用し、より効率的で信頼性の高いコラボレーション ソフトウェア開発ワークフローを実現できます。