Ferramenta de migração do Bzlmod

Para simplificar o processo de migração de WORKSPACE para Bzlmod, recomendamos o uso do script de migração. Essa ferramenta auxiliar automatiza muitas das etapas envolvidas na migração do sistema de gerenciamento de dependências externas.

Observação: se você quiser testar a migração do Bzlmod orientada por IA, consulte Configuração do agente de migração do Bzlmod.

Funcionalidade principal

As principais funções do script são:

  • Coleta de informações de dependência: analisa o arquivo WORKSPACE do projeto para identificar repositórios externos usados por destinos de build especificados, usando a flag experimental_repository_resolved_file do Bazel para gerar um arquivo de dependências resolvidas que contém essas informações.
  • Identificação de dependências diretas:usa bazel query para determinar quais repositórios são dependências diretas dos destinos especificados.
  • Migração para o Bzlmod:traduz as dependências relevantes do WORKSPACE para os equivalentes do Bzlmod. Esse processo é realizado em duas etapas:
    1. Introduza todas as dependências diretas identificadas no arquivo MODULE.bazel.
    2. Crie destinos especificados com o Bzlmod ativado e identifique e corrija erros reconhecíveis de forma iterativa. Essa etapa é necessária porque algumas dependências podem estar ausentes na primeira etapa.
  • Geração de um relatório de migração:cria um arquivo migration_info.md que documenta o processo de migração. Esse relatório inclui uma lista de dependências diretas, as declarações do Bzlmod geradas e todas as etapas manuais que podem ser necessárias para concluir a migração.

A ferramenta de migração oferece suporte a:

  • Dependências disponíveis no Registro central do Bazel
  • Regras de repositório personalizadas definidas pelo usuário
  • Dependências do gerenciador de pacotes
    • Maven
    • Go
    • Python

Observações importantes:

  • A ferramenta de migração é um utilitário de melhor esforço. Sempre verifique se as recomendações estão corretas.
  • Use a ferramenta de migração com o Bazel 7 (não é compatível com o Bazel 8).

Como usar a ferramenta de migração

Antes de começar:

  • Faça upgrade para a versão mais recente do Bazel 7, que oferece suporte robusto ao WORKSPACE e ao Bzlmod.
  • Verifique se o comando a seguir é executado corretamente para os principais destinos de build do projeto:

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

Comando para executar o script

Depois que os pré-requisitos forem atendidos, execute os comandos a seguir para usar a ferramenta de migração:

# 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>

Arquivos gerados por esse script

  • MODULE.bazel : o arquivo de manifesto central do Bzlmod, que declara os metadados do projeto e as dependências diretas em outros módulos do Bazel.
  • migration_info.md : um arquivo que fornece instruções detalhadas sobre como a ferramenta de migração foi executada, projetada para ajudar na conclusão manual do processo de migração, se necessário.
  • resolved_deps.py : contém uma lista abrangente das dependências externas do projeto, gerada pela análise do arquivo WORKSPACE do projeto, servindo como referência durante a transição.
  • query_direct_deps : contém informações relevantes para a migração sobre os destinos utilizados, obtidas invocando o Bazel com --output=build no arquivo WORKSPACE do projeto. Esse arquivo é consumido principalmente pelo script de migração.
  • extension_for_XXX : um arquivo que contém uma definição de extensão de módulo. A ferramenta de migração gera esses arquivos para dependências que não são módulos padrão do Bazel, mas podem ser gerenciadas usando as extensões de módulo do Bzlmod.

Sinalizações

As flags disponíveis nesses scripts de migração são:

  • --t/--target: destinos a serem migrados. Essa flag é repetível, e os destinos são acumulados.
  • --i/--initial: exclui os arquivos MODULE.bazel, resolved_deps.py, migration_info.md e começa do zero. Detecta dependências diretas, as introduz no MODULE.bazel e executa novamente a geração de dependências resolvidas.

Limpeza pós-migração

  • Exclua migration_info.md, resolved_deps.py e query_direct_deps.
  • Limpe os comentários do arquivo MODULE.bazel que foram usados para a migração, como # -- bazel_dep definitions -- #.

Exemplo de migração

Para ver o script de migração em ação, considere o cenário a seguir quando as dependências do Python, do Maven e do Go são declaradas no arquivo WORKSPACE.

Clique aqui para ver o arquivo 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",
    ],
)

Além disso, para demonstrar o uso da extensão do módulo, a macro personalizada é invocada do WORKSPACE e definida em my_custom_macro.bzl.

Clique aqui para ver o arquivo 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)

O objetivo final é ter o arquivo MODULE.bazel e excluir o arquivo WORKSPACE, sem afetar a experiência do usuário.

A primeira etapa é seguir Como usar a ferramenta de migração, que consiste principalmente em verificar a versão do Bazel (ela precisa ser a 7) e adicionar um alias ao script de migração.

Em seguida, a execução de migrate2bzlmod -t=//... gera:

  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.

que fornece as seguintes informações importantes:

  • Gera o arquivo ./resolved_deps.py, que contém informações sobre todos os repositórios externos declarados e carregados usando o arquivo WORKSPACE.
  • A palavra-chave RESOLVED descreve todas as dependências resolvidas pela ferramenta e adicionadas ao arquivo MODULE.bazel.
  • A palavra-chave IMPORTANT descreve informações significativas que valem a pena investir tempo.
  • Todas as dependências foram resolvidas neste exemplo, pelo menos com a flag --nobuild.
  • É importante executar o build completo (comando especificado) e corrigir manualmente possíveis erros (por exemplo, o conjunto de ferramentas não registrado corretamente).
  • O arquivo migration_info.md contém detalhes sobre a migração. Confira os detalhes nesta seção.

Transformações

Esta seção ilustra a migração de código do arquivo WORKSPACE para MODULE.bazel.

WORKSPACE - Módulo do 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 - Módulo do Bazel

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

WORKSPACE - Extensão do 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 - Extensão do 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 - Extensão do 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 - Extensão do 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 - Extensão do 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 - Extensão do 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 - Regra de repositório

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 - Regra de repositório

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 - Extensão do módulo

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

my_custom_macro(
    name = "my_custom_repo",
)

MODULE.bazel - Extensão do módulo

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)

Dicas de depuração

Esta seção fornece comandos e informações úteis para ajudar a depurar problemas que podem surgir durante a migração do Bzlmod.

Dicas úteis

  • Substituir versão: não é raro que o upgrade da versão de uma dependência cause problemas. O Bzlmod pode mudar a versão da dependência devido ao algoritmo MVS. Para usar a mesma versão ou uma versão semelhante à do WORKSPACE, substitua-a por single_version_override. Isso é útil para depurar diferenças entre o WORKSPACE e o Bzlmod, mas não confie nesse recurso a longo prazo.

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

  • Use o comando bazel mod.

    • Verifique a versão de um repositório especificado com o comando show_repo. Por exemplo:

      bazel mod show_repo @rules_python

    • Verifique as informações sobre uma extensão de módulo com o comando show_extension. Por exemplo:

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

  • Use o modo de fornecedor para criar uma cópia local de um repositório quando quiser monitorar ou controlar a origem dele. Por exemplo:

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

Geração de relatórios de migração

Esse arquivo é atualizado a cada execução do script de migração ou gerado do zero se for a primeira execução ou se a --i flag for usada. O relatório contém:

  • Comando para testes locais.
  • Lista de dependências diretas (pelo menos as que são usadas diretamente no projeto).
  • Para cada dependência, um menu suspenso para verificar onde o repositório foi declarado no arquivo WORKSPACE, o que é particularmente útil para a depuração. Ele aparece como:

    > Click here to see where and how the repo was declared in the WORKSPACE
    file
  • Para cada dependência, como ela foi implementada no arquivo MODULE.bazel. No exemplo de migração anterior, isso seria assim:

    1. Dependência do módulo do 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")`
      • O script vai usar automaticamente a perfect name match se a encontrar. Em caso de erro, verifique se o nome foi adicionado corretamente.
    2. Extensão do 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. Extensão do Maven: Migration of org.antlr (px_deps):

      maven.artifact(
          name = "px_deps",
          group = "org.antlr",
          artifact = "antlr4",
          version = "4.11.1"
      )
    4. Extensão do 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",
          ],
      )
      • Ele foi introduzido como um módulo Go com a ajuda de go.mod. Se go.mod e go.sum não estiverem disponíveis, o módulo Go será adicionado diretamente ao arquivo MODULE.bazel.
      • gazelle_override é usado para adicionar diretivas específicas.

Feedback

Se você quiser contribuir, crie um problema ou PR no bazel-central-registry.