Bzlmod 遷移工具

WORKSPACE 遷移至 Bzlmod 的過程通常很複雜,因此強烈建議使用遷移指令碼,這項輔助工具會自動執行許多步驟,協助您遷移外部依附元件管理系統。

注意:如要試用 AI 輔助的 Bzlmod 遷移功能,請參閱「Bzlmod 遷移代理程式設定」。

核心功能

這個指令碼的主要功能如下:

  • 收集依附元件資訊:分析專案的 WORKSPACE 檔案,找出指定建構目標使用的外部存放區,並使用 Bazel 的 experimental_repository_resolved_file 標記產生含有這項資訊的已解析依附元件檔案。
  • 找出直接依附元件:使用 bazel query 判斷指定目標的直接依附元件是哪些存放區。
  • 遷移至 Bzlmod:將相關 WORKSPACE 依附元件轉換為 Bzlmod 對應項目。這個過程分為兩步驟:
    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 
migrate2bzlmod -t 

這個指令碼產生的檔案

  • MODULE.bazel - Bzlmod 的中央資訊清單檔案,用於宣告專案的中繼資料,以及對其他 Bazel 模組的直接依附元件。
  • migration_info.md - 檔案:提供如何執行遷移工具的逐步說明,協助手動完成遷移程序 (如有必要)。
  • resolved_deps.py:包含專案外部依附元件的完整清單,透過分析專案的 WORKSPACE 檔案產生,可在轉換期間做為參考。
  • query_direct_deps - 包含與所用目標相關的遷移資訊,方法是在專案的 WORKSPACE 檔案中叫用 Bazel --output=build。這個檔案主要由遷移指令碼使用。
  • 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 -- #

遷移範例

如要查看遷移指令碼的實際運作情形,請參考下列情境:在 WORKSPACE 檔案中宣告 Python、Maven 和 Go 依附元件。

按一下這裡查看 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 Extension

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 Extension

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 rule

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 - Module extension

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

my_custom_macro(
    name = "my_custom_repo",
)

MODULE.bazel - Module extension

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 遷移期間可能發生的問題。

實用提示

  • 覆寫版本 - 升級依附元件版本時,有時會發生問題。由於 MVS 演算法,Bzlmod 可能會變更依附元件的版本。如要使用與 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. 前往擴充功能 - 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。