Ferramenta de migração do Bzlmod

Para simplificar o processo, muitas vezes complexo, de migração de WORKSPACE para Bzlmod, é altamente recomendável usar o script de migração. Essa ferramenta auxiliar automatiza muitas das etapas envolvidas na migração do seu sistema externo de gerenciamento de dependências.

Observação: se você quiser testar a migração do Bzlmod com tecnologia de IA, confira 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:análise do 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 com essas informações.
  • Identificação de dependências diretas:uso de bazel query para determinar quais repositórios são dependências diretas dos destinos especificados.
  • Migração para o Bzlmod:tradução das dependências WORKSPACE relevantes 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 os 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 faltando na primeira etapa.
  • Gerar um relatório de migração:criar 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 Bzlmod geradas e as etapas manuais que podem ser necessárias para concluir a migração.

A ferramenta de migração é compatível com:

  • Dependências disponíveis no Registro central do Bazel
  • Regras personalizadas do repositório 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 para WORKSPACE e Bzlmod.
  • Verifique se o seguinte comando é 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 
migrate2bzlmod -t 

Arquivos gerados por esse script

  • MODULE.bazel: o arquivo de manifesto central do Bzlmod, que declara os metadados do projeto e suas dependências diretas em outros módulos do Bazel.
  • migration_info.md: um arquivo com instruções detalhadas sobre como a ferramenta de migração foi executada, projetado 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 ao invocar o Bazel com --output=build no arquivo WORKSPACE do projeto. Esse arquivo é usado 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 pode ser repetida, e os destinos são acumulados.
  • --i/--initial: exclui os arquivos MODULE.bazel, resolved_deps.py e migration_info.md e começa do zero. Detecta dependências diretas, as introduz em 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 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 seguinte cenário em que 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 de 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 (precisa ser o Bazel 7) e adicionar um alias ao script de migração.

Em seguida, executar 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.
  • Neste exemplo, todas as dependências foram resolvidas, pelo menos com a flag --nobuild.
  • É importante executar o build completo (comando especificado) e corrigir manualmente possíveis erros (por exemplo, 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 do 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 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 Extension

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 de 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 semelhante à do WORKSPACE, substitua-a por single_version_override. Isso é útil para depurar diferenças entre WORKSPACE e 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. Exemplo:

      bazel mod show_repo @rules_python

    • Confira informações sobre uma extensão de módulo com o comando show_extension. Exemplo:

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

  • Use o modo do fornecedor para criar uma cópia local de um repositório quando quiser monitorar ou controlar a origem dele. 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 flag --i for usada. O relatório contém:

  • Comando para teste local.
  • 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 é especialmente útil para a depuração. Você pode entender isso 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 o perfect name match se o 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 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 quiser contribuir, crie um problema ou PR em bazel-central-registry.