Bzlmod 移行ツール

WORKSPACE から Bzlmod への移行は複雑になることが多いため、 移行スクリプトを使用することを強くおすすめします。このヘルパーツールは、外部依存関係管理システムの移行に伴う多くの手順を自動化します。

: AI を活用した Bzlmod 移行を試す場合は、Bzlmod 移行エージェントの設定をご覧ください。

コア機能

スクリプトの主な機能は次のとおりです。

  • 依存関係情報の収集: プロジェクトの WORKSPACE ファイルを分析して、指定されたビルドターゲットで使用される外部リポジトリを特定し、 Bazel の experimental_repository_resolved_file フラグを使用して、この情報を含む解決済み依存関係ファイルを生成します。
  • 直接的な依存関係の特定: bazel query を使用して、指定されたターゲットの直接的な依存関係であるリポジトリを特定します。
  • Bzlmod への移行: 関連する WORKSPACE の依存関係を Bzlmod の同等のものに変換します。これは 2 段階のプロセスです。
    1. 特定された直接的な依存関係をすべて MODULE.bazel ファイルに導入します。
    2. Bzlmod を有効にして指定されたターゲットをビルドし、認識可能なエラーを特定して修正します。この手順が必要なのは、最初の手順で一部の依存関係が欠落している可能性があるためです。
  • 移行レポートの生成: 移行プロセスを文書化した migration_info.md ファイルを作成します。このレポートには、直接的な依存関係のリスト、生成された Bzlmod 宣言、移行を完了するために必要な手動の手順が含まれています。

移行ツールは以下をサポートしています。

  • Bazel Central Registry で利用可能な依存関係
  • ユーザー定義のカスタム リポジトリ ルール
  • パッケージ マネージャーの依存関係
    • Maven
    • Go
    • Python

注意事項:

  • 移行ツールはベスト エフォート型のユーティリティです。推奨事項が正しいかどうか、必ず再確認してください。
  • 移行ツールは Bazel 7 で使用してください(Bazel 8 ではサポートされていません)。

移行ツールの使用方法

始める前に:

  • 最新の Bazel 7 リリースにアップグレードします。このリリースでは、WORKSPACE と Bzlmod の両方が強力にサポートされています。
  • プロジェクトのメインビルド ターゲットで次のコマンドが正常に実行されることを確認します。

    bazel build --nobuild --enable_workspace --noenable_bzlmod <targets>
    

スクリプトを実行するコマンド

前提条件を満たしたら、次のコマンドを実行して移行ツールを使用します。

# Clone the Bazel Central Registry repository
git clone https://github.com/bazelbuild/bazel-central-registry.git
cd bazel-central-registry

# Build the migration tool
bazel build //tools:migrate_to_bzlmod

# Create a convenient alias for the tool
alias migrate2bzlmod=$(realpath ./bazel-bin/tools/migrate_to_bzlmod)

# Navigate to your project's root directory and run the tool
cd <your project root>
migrate2bzlmod -t <targets>

このスクリプトで生成されるファイル

  • MODULE.bazel - Bzlmod の中央マニフェスト ファイル。 プロジェクトのメタデータと、他の Bazel モジュールに対する直接的な依存関係を宣言します。
  • migration_info.md - 移行ツールの実行方法に関する手順ガイドを提供するファイル。必要に応じて、移行プロセスを手動で完了する際に役立ちます。
  • resolved_deps.py - プロジェクトの外部 依存関係の包括的なリストが含まれています。プロジェクトの WORKSPACE ファイルを分析して生成され、移行時の参照として使用されます。
  • query_direct_deps - プロジェクトの WORKSPACEファイルで--output=buildを指定して Bazel を呼び出すことで取得した、使用されるターゲットに関する移行関連情報が含まれています。このファイルは主に移行スクリプトで使用されます。
  • extension_for_XXX - モジュール拡張機能の定義を含むファイル。移行ツールは、標準の Bazel モジュールではないが、Bzlmod の モジュール 拡張機能を使用して管理できる依存関係に対して、これらのファイルを生成します。

フラグ

この移行スクリプトで使用できるフラグは次のとおりです。

  • --t/--target: 移行するターゲット。このフラグは繰り返し使用でき、ターゲットが累積されます。
  • --i/--initial: MODULE.bazelresolved_deps.pymigration_info.md ファイルを削除して、最初からやり直します。直接的な 依存関係を検出し、MODULE.bazel に導入して、 解決済み依存関係の生成を再実行します。

移行後のクリーンアップ

  • migration_info.mdresolved_deps.pyquery_direct_deps を削除します。
  • MODULE.bazel ファイルから、移行に使用されたコメント(# -- bazel_dep definitions -- # など)をクリーンアップします。

移行の例

移行スクリプトの動作を確認するには、Python、Maven、Go の依存関係が WORKSPACE ファイルで宣言されている次のシナリオを考えてみましょう。

ここをクリックして WORKSPACE ファイルを表示

workspace(name="example")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load(":my_custom_macro.bzl", "my_custom_macro")

http_archive(
    name = "rules_cc",
    sha256 = "b8b918a85f9144c01f6cfe0f45e4f2838c7413961a8ff23bc0c6cdf8bb07a3b6",
    strip_prefix = "rules_cc-0.1.5",
    urls = ["https://github.com/bazelbuild/rules_cc/releases/download/0.1.5/rules_cc-0.1.5.tar.gz"],
)

# Module dependency
# -------------------
http_archive(
    name = "rules_shell",
    sha256 = "3e114424a5c7e4fd43e0133cc6ecdfe54e45ae8affa14fadd839f29901424043",
    strip_prefix = "rules_shell-0.4.0",
    url = "https://github.com/bazelbuild/rules_shell/releases/download/v0.4.0/rules_shell-v0.4.0.tar.gz",
)

# Repo rule
# -------------------
http_archive(
    name = "com_github_cockroachdb_cockroach",
    sha256 = "6c3568ef244ce6b874694eeeecb83ed4f5d5dff6cf037c952ecde76828a6c502",
    strip_prefix = "cockroach-22.1.6",
    url = "https://github.com/cockroachdb/cockroach/archive/v22.1.6.tar.gz",
)

# Module extension
# -------------------
# Macro which invokes repository_rule
my_custom_macro(
    name = "my_custom_repo",
)

# Go dependencies
# -------------------
http_archive(
    name = "io_bazel_rules_go",
    integrity = "sha256-M6zErg9wUC20uJPJ/B3Xqb+ZjCPn/yxFF3QdQEmpdvg=",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.48.0/rules_go-v0.48.0.zip",
        "https://github.com/bazelbuild/rules_go/releases/download/v0.48.0/rules_go-v0.48.0.zip",
    ],
)

http_archive(
    name = "bazel_gazelle",
    integrity = "sha256-12v3pg/YsFBEQJDfooN6Tq+YKeEWVhjuNdzspcvfWNU=",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.37.0/bazel-gazelle-v0.37.0.tar.gz",
        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.37.0/bazel-gazelle-v0.37.0.tar.gz",
    ],
)

load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")

go_rules_dependencies()
go_register_toolchains(version = "1.23.1")
gazelle_dependencies()

go_repository(
    name = "org_golang_x_net",
    importpath = "golang.org/x/net",
    sum = "h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=",
    version = "v0.0.0-20190311183353-d8887717615a",
    build_file_proto_mode = "disable",
    build_naming_convention = "import",
)

# Python dependencies
# -------------------
http_archive(
    name = "rules_python",
    integrity = "sha256-qDdnnxOC8mlowe5vg5x9r5B5qlMSgGmh8oFd7KpjcwQ=",
    strip_prefix = "rules_python-1.4.0",
    url = "https://github.com/bazelbuild/rules_python/releases/download/1.4.0/rules_python-1.4.0.tar.gz",
)

load("@rules_python//python:repositories.bzl", "py_repositories")
py_repositories()

load("@rules_python//python:pip.bzl", "pip_parse")
pip_parse(
   name = "my_python_deps",
   requirements_lock = "@example//:requirements_lock.txt",
)

load("@my_python_deps//:requirements.bzl", "install_deps")
install_deps()

load("@rules_python//python:repositories.bzl", "python_register_toolchains")
python_register_toolchains(
    name = "python_3_11",
    python_version = "3.11",
)

# Maven dependencies
# __________________

RULES_JVM_EXTERNAL_TAG = "4.5"
RULES_JVM_EXTERNAL_SHA = "b17d7388feb9bfa7f2fa09031b32707df529f26c91ab9e5d909eb1676badd9a6"

http_archive(
    name = "rules_jvm_external",
    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
    sha256 = RULES_JVM_EXTERNAL_SHA,
    url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)

load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
rules_jvm_external_deps()
load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")
rules_jvm_external_setup()

load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_install(
    name = "px_deps",
    artifacts = [
        "org.antlr:antlr4:4.11.1",
    ],
    repositories = [
        "https://repo1.maven.org/maven2",
    ],
)

また、モジュール拡張機能の使用方法を示すために、カスタム マクロが WORKSPACE から呼び出され、my_custom_macro.bzl で定義されています。

ここをクリックして my_custom_macro.bzl ファイルを表示

"""Repo rule and macro used for testing"""

def _test_repo_rule_impl(repository_ctx):
    repository_ctx.file(
        "BUILD",
        content = """
genrule(
    name = "foo",
    outs = ["rule_name.out"],
    cmd = "touch $@",
    visibility = ["//visibility:public"],
)
"""
    )

_test_repo_rule = repository_rule(
    implementation = _test_repo_rule_impl,
)

def my_custom_macro(name):
    _test_repo_rule(name = name)

最終的な目標は、ユーザー エクスペリエンスに影響を与えることなく、MODULE.bazel ファイルを作成して WORKSPACE ファイルを削除することです。

最初の手順は、移行ツールの使用方法 に沿って、Bazel のバージョン (Bazel 7 である必要があります)を確認し、移行スクリプトにエイリアスを追加することです。

次に、migrate2bzlmod -t=//... を実行すると、次の出力が表示されます。

  bazel 7.6.1

  Generating ./resolved_deps.py file - It might take a while...

  RESOLVED: rules_java has been introduced as a Bazel module.
  RESOLVED: bazel_gazelle has been introduced as a Bazel module.
  RESOLVED: io_bazel_rules_go has been introduced as a Bazel module.
  RESOLVED: rules_python has been introduced as a Bazel module.
  IMPORTANT: 3.11 is used as a default python version. If you need a different version, please change it manually and then rerun the migration tool.
  RESOLVED: my_python_deps has been introduced as python extension.
  RESOLVED: org_golang_x_net has been introduced as go extension.
  RESOLVED: rules_jvm_external has been introduced as a Bazel module.
  RESOLVED: org.antlr has been introduced as maven extension.
  RESOLVED: rules_shell has been introduced as a Bazel module.

  Congratulations! All external repositories needed for building //... are available with Bzlmod!
  IMPORTANT: Fix potential build time issues by running the following command:
      bazel build --enable_bzlmod --noenable_workspace //...

  IMPORTANT: For details about the migration process, check `migration_info.md` file.

これにより、次の重要な情報が得られます。

  • ./resolved_deps.py ファイルが生成されます。このファイルには、WORKSPACE ファイルを使用して宣言および読み込まれたすべての外部リポジトリに関する情報が含まれています。
  • RESOLVED キーワードは、ツールによって解決され、MODULE.bazel ファイルに追加されたすべての依存関係を表します。
  • IMPORTANT キーワードは、時間をかける価値のある重要な情報を表します。
  • この例では、少なくとも --nobuild フラグを使用して、すべての依存関係が解決されています。
  • 完全なビルド(指定されたコマンド)を実行し、潜在的なエラー(ツールチェーンが正しく登録されていないなど)を手動で修正することが重要です。
  • migration_info.md ファイルには、移行の詳細が含まれています。詳細については、こちらのセクション をご覧ください

変換

このセクションでは、WORKSPACE ファイルから MODULE.bazel へのコードの移行について説明します。

WORKSPACE - Bazel モジュール

http_archive(
    name = "rules_shell",
    sha256 = "3e114424a5c7e4fd43e0133cc6ecdfe54e45ae8affa14fadd839f29901424043",
    strip_prefix = "rules_shell-0.4.0",
    url = "https://github.com/bazelbuild/rules_shell/releases/download/v0.4.0/rules_shell-v0.4.0.tar.gz",
)

MODULE.bazel - Bazel モジュール

bazel_dep(name = "rules_shell", version = "0.6.1")

WORKSPACE - Go 拡張機能

http_archive(
    name = "io_bazel_rules_go",
    integrity = "sha256-M6zErg9wUC20uJPJ/B3Xqb+ZjCPn/yxFF3QdQEmpdvg=",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.48.0/rules_go-v0.48.0.zip",
        "https://github.com/bazelbuild/rules_go/releases/download/v0.48.0/rules_go-v0.48.0.zip",
    ],
)
http_archive(
    name = "bazel_gazelle",
    integrity = "sha256-12v3pg/YsFBEQJDfooN6Tq+YKeEWVhjuNdzspcvfWNU=",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.37.0/bazel-gazelle-v0.37.0.tar.gz",
        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.37.0/bazel-gazelle-v0.37.0.tar.gz",
    ],
)

load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")

go_rules_dependencies()
go_register_toolchains(version = "1.23.1")
gazelle_dependencies()

go_repository(
    name = "org_golang_x_net",
    importpath = "golang.org/x/net",
    sum = "h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=",
    version = "v0.0.0-20190311183353-d8887717615a",
    build_file_proto_mode = "disable",
    build_naming_convention = "import",
)

MODULE.bazel - Go 拡張機能

go_deps = use_extension("@bazel_gazelle//:extensions.bzl", "go_deps")
go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk")

go_deps.from_file(go_mod = "//:go.mod")
use_repo(go_deps, "org_golang_x_net")
go_sdk.from_file(go_mod = "//:go.mod")

go_deps.gazelle_override(
    path = "golang.org/x/net",
    directives = [
        "gazelle:proto disable",
         "gazelle:go_naming_convention import",
    ],
)

WORKSPACE - Python 拡張機能

http_archive(
    name = "rules_python",
    integrity = "sha256-qDdnnxOC8mlowe5vg5x9r5B5qlMSgGmh8oFd7KpjcwQ=",
    strip_prefix = "rules_python-1.4.0",
    url = "https://github.com/bazelbuild/rules_python/releases/download/1.4.0/rules_python-1.4.0.tar.gz",
)

load("@rules_python//python:repositories.bzl", "py_repositories")
py_repositories()

load("@rules_python//python:pip.bzl", "pip_parse")
pip_parse(
   name = "my_python_deps",
   requirements_lock = "@example//:requirements_lock.txt",
)

load("@my_python_deps//:requirements.bzl", "install_deps")
install_deps()

load("@rules_python//python:repositories.bzl", "python_register_toolchains")
python_register_toolchains(
    name = "python_3_11",
    python_version = "3.11",
)

MODULE.bazel - Python 拡張機能

pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip.parse(
    hub_name = "my_python_deps",
    python_version = "3.11",
    requirements_lock = "//:requirements_lock.txt",
)
use_repo(pip, "my_python_deps")

python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.defaults(python_version = "3.11")
python.toolchain(python_version = "3.11")

WORKSPACE - Maven 拡張機能

RULES_JVM_EXTERNAL_TAG = "4.5"
RULES_JVM_EXTERNAL_SHA = "b17d7388feb9bfa7f2fa09031b32707df529f26c91ab9e5d909eb1676badd9a6"

http_archive(
    name = "rules_jvm_external",
    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
    sha256 = RULES_JVM_EXTERNAL_SHA,
    url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)

load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
rules_jvm_external_deps()
load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")
rules_jvm_external_setup()

load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_install(
    name = "px_deps",
    artifacts = [
        "org.antlr:antlr4:4.11.1",
    ],
    repositories = [
        "https://repo1.maven.org/maven2",
    ],
)

MODULE.bazel - Maven 拡張機能

bazel_dep(name = "rules_jvm_external", version = "6.8")

maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
use_repo(maven, "px_deps")

maven.artifact(
    name = "px_deps",
    group = "org.antlr",
    artifact = "antlr4",
    version = "4.11.1"
)

WORKSPACE - Repo ルール

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "com_github_cockroachdb_cockroach",
    sha256 = "6c3568ef244ce6b874694eeeecb83ed4f5d5dff6cf037c952ecde76828a6c502",
    strip_prefix = "cockroach-22.1.6",
    url = "https://github.com/cockroachdb/cockroach/archive/v22.1.6.tar.gz",
)

MODULE.bazel - Repo ルール

http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
  name = "com_github_cockroachdb_cockroach",
  url = "https://github.com/cockroachdb/cockroach/archive/v22.1.6.tar.gz",
  sha256 = "6c3568ef244ce6b874694eeeecb83ed4f5d5dff6cf037c952ecde76828a6c502",
  strip_prefix = "cockroach-22.1.6",
)

WORKSPACE - モジュール拡張機能

load(":my_custom_macro.bzl", "my_custom_macro")

my_custom_macro(
    name = "my_custom_repo",
)

MODULE.bazel - モジュール拡張機能

extension_for_my_custom_macro = use_extension("//:extension_for_my_custom_macro.bzl", "extension_for_my_custom_macro")
use_repo(extension_for_my_custom_macro, "my_custom_repo")

extension_for_my_custom_macro.bzl

load("//:my_custom_macro.bzl", "my_custom_macro")

def _extension_for_my_custom_macro_impl(ctx):
  my_custom_macro(
    name = "my_custom_repo",
  )

extension_for_my_custom_macro = module_extension(implementation = _extension_for_my_custom_macro_impl)

デバッグのヒント

このセクションでは、Bzlmod 移行中に発生する可能性のある問題のデバッグに役立つコマンドと情報を提供します。

ヒント

  • バージョンのオーバーライド - 依存関係のバージョンをアップグレードすると、問題が発生することがあります。Bzlmod は、 MVS アルゴリズムにより依存関係のバージョンを変更する可能性があります。 WORKSPACE と同じバージョンまたは類似のバージョンを使用するには、 single_version_override でオーバーライドします。 これは WORKSPACE と Bzlmod の違いをデバッグするのに役立ちますが、長期的にこの機能に依存することは避けてください。

    single_version_override(module_name = "{dep_name}", version = "{version}")

  • bazel mod コマンドを使用します。

    • show_repo コマンドを使用して、指定されたリポジトリのバージョンを確認します。次に例を示します。

      bazel mod show_repo @rules_python

    • show_extension コマンドを使用して、モジュール拡張機能に関する情報を確認します。次に例を示します。

      bazel mod show_extension @rules_python//python/extensions:pip.bzl%pip

  • リポジトリのソースをモニタリングまたは制御する場合は、ベンダーモードを使用してリポジトリのローカルコピーを作成します。次に例を示します。

    bazel vendor --enable_bzlmod --vendor_dir=vendor_src --repo=@protobuf

移行レポートの生成

このファイルは、移行スクリプトを実行するたびに更新されます。初回実行時、または --i フラグが使用された場合は、 最初から生成されます。レポートには次の情報が含まれます。

  • ローカルテストのコマンド。
  • 直接的な依存関係のリスト(少なくともプロジェクトで直接使用されているもの)。
  • 依存関係ごとに、リポジトリが WORKSPACE ファイルで宣言された場所を確認するためのプルダウン メニュー。これはデバッグに特に役立ちます。次のように表示されます。

    > Click here to see where and how the repo was declared in the WORKSPACE
    file
  • 依存関係ごとに、MODULE.bazel ファイルでどのように実装されたか。前の 移行の例では、次のようになります。

    1. Bazel モジュールの依存関係 - Migration of rules_python

      Found perfect name match in BCR: rules_python
      Found partially name matches in BCR: rules_python_gazelle_plugin
      
      It has been introduced as a Bazel module:
          `bazel_dep(name = "rules_python", version = "1.6.1")`
      • スクリプトは、perfect name match が見つかった場合は自動的に使用します。エラーが発生した場合は、名前が正しく追加されていることを再確認してください。
    2. Python 拡張機能 - Migration of my_python_deps

      pip.parse(
          hub_name = "my_python_deps",
          requirements_lock = "//:requirements_lock.txt",
          python_version = "3.11",
      )
      use_repo(pip, "my_python_deps")
    3. Maven 拡張機能 - Migration of org.antlr (px_deps):

      maven.artifact(
          name = "px_deps",
          group = "org.antlr",
          artifact = "antlr4",
          version = "4.11.1"
      )
    4. Go 拡張機能 - Migration of org_golang_x_net

      go_deps.from_file(go_mod = "//:go.mod")
      go_sdk.from_file(go_mod = "//:go.mod")
      
      go_deps.gazelle_override(
          path = "golang.org/x/net",
          directives = [
              "gazelle:proto disable",
              "gazelle:go_naming_convention import",
          ],
      )
      • go.mod を使用して Go モジュールとして導入されました。go.modgo.sum がない場合は、Go モジュールが MODULE.bazel ファイルに直接追加されます。
      • gazelle_override は、特定のディレクティブを追加するために使用されます。

フィードバック

貢献したい場合は、 bazel-central-registry で Issue または PR を作成してください。