Herramienta de migración de Bzlmod

Para simplificar el proceso, a menudo complejo, de migrar de WORKSPACE a Bzlmod, se recomienda usar la secuencia de comandos de migración. Esta herramienta de ayuda automatiza muchos de los pasos involucrados en la migración de tu sistema externo de administración de dependencias.

Nota: Si quieres probar la migración a Bzlmod impulsada por IA, consulta Configuración del agente de migración a Bzlmod.

Funcionalidad principal

Las funciones principales de la secuencia de comandos son las siguientes:

  • Recopilación de información de dependencias: Se analiza el archivo WORKSPACE de tu proyecto para identificar los repositorios externos que usan los destinos de compilación especificados, con la marca experimental_repository_resolved_file de Bazel para generar un archivo de dependencias resueltas que contenga esta información.
  • Identificación de dependencias directas: Uso de bazel query para determinar qué repositorios son dependencias directas para los destinos especificados.
  • Migración a Bzlmod: Traducción de las dependencias de WORKSPACE pertinentes a sus equivalentes de Bzlmod Este es un proceso de dos pasos:
    1. Introduce todas las dependencias directas identificadas en el archivo MODULE.bazel.
    2. Compila los destinos especificados con Bzlmod habilitado y, luego, identifica y corrige de forma iterativa los errores reconocibles. Este paso es necesario, ya que es posible que falten algunas dependencias en el primer paso.
  • Generación de un informe de migración: Creación de un archivo migration_info.md que documenta el proceso de migración. Este informe incluye una lista de las dependencias directas, las declaraciones de Bzlmod generadas y los pasos manuales que pueden ser necesarios para completar la migración.

La herramienta de migración admite lo siguiente:

  • Dependencias disponibles en el registro central de Bazel
  • Reglas de repositorio personalizadas definidas por el usuario
  • Dependencias del administrador de paquetes
    • Maven
    • Go
    • Python

Notas importantes:

  • La herramienta de migración es una utilidad que se ofrece de la mejor manera posible. Siempre verifica que sus recomendaciones sean correctas.
  • Usa la herramienta de migración con Bazel 7 (no es compatible con Bazel 8).

Cómo usar la herramienta de migración

Antes de comenzar:

  • Actualiza a la versión más reciente de Bazel 7, que proporciona compatibilidad sólida con WORKSPACE y Bzlmod.
  • Verifica que el siguiente comando se ejecute correctamente para los principales destinos de compilación de tu proyecto:

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

Comando para ejecutar el script

Una vez que se cumplan los requisitos previos, ejecuta los siguientes comandos para usar la herramienta de migración:

# 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 

Archivos generados por este script

  • MODULE.bazel: Es el archivo de manifiesto central de Bzlmod, que declara los metadatos del proyecto y sus dependencias directas de otros módulos de Bazel.
  • migration_info.md: Es un archivo que proporciona instrucciones paso a paso sobre cómo se ejecutó la herramienta de migración y que está diseñado para ayudar a completar manualmente el proceso de migración, si es necesario.
  • resolved_deps.py: Contiene una lista completa de las dependencias externas del proyecto, que se genera analizando el archivo WORKSPACE del proyecto y sirve como referencia durante la transición.
  • query_direct_deps: Contiene información relevante para la migración sobre los destinos utilizados, que se obtiene invocando Bazel con --output=build en el archivo WORKSPACE del proyecto. El script de migración es el que consume principalmente este archivo.
  • extension_for_XXX: Es un archivo que contiene una definición de extensión del módulo. La herramienta de migración genera estos archivos para las dependencias que no son módulos estándar de Bazel, pero que se pueden administrar con las extensiones de módulos de Bzlmod.

Marcas

Las marcas disponibles en estas secuencias de comandos de migración son las siguientes:

  • --t/--target: Son los destinos a los que se migrarán los datos. Esta marca se puede repetir y los destinos se acumulan.
  • --i/--initial: Borra los archivos MODULE.bazel, resolved_deps.py y migration_info.md, y comienza desde cero. Detecta las dependencias directas, las introduce en MODULE.bazel y vuelve a ejecutar la generación de las dependencias resueltas.

Limpieza posterior a la migración

  • Se borraron migration_info.md, resolved_deps.py y query_direct_deps.
  • Limpia los comentarios del archivo MODULE.bazel que se usaron para la migración, como # -- bazel_dep definitions -- #.

Ejemplo de migración

Para ver el script de migración en acción, considera la siguiente situación en la que se declaran las dependencias de Python, Maven y Go en el archivo WORKSPACE.

Haz clic aquí para ver el archivo 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",
    ],
)

Además, para demostrar el uso de la extensión del módulo, se invoca la macro personalizada desde WORKSPACE y se define en my_custom_macro.bzl.

Haz clic aquí para ver el archivo 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)

El objetivo final es tener el archivo MODULE.bazel y borrar el archivo WORKSPACE, sin afectar la experiencia del usuario.

El primer paso es seguir las instrucciones de Cómo usar la herramienta de migración, que consiste principalmente en verificar la versión de Bazel (debe ser Bazel 7) y agregar un alias a la secuencia de comandos de migración.

Luego, al ejecutar migrate2bzlmod -t=//..., se obtienen los siguientes resultados:

  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 proporciona la siguiente información importante:

  • Genera el archivo ./resolved_deps.py, que contiene información sobre todos los repositorios externos declarados y cargados con tu archivo WORKSPACE.
  • La palabra clave RESOLVED describe todas las dependencias que resuelve la herramienta y que se agregan al archivo MODULE.bazel.
  • La palabra clave IMPORTANT describe información importante en la que vale la pena invertir tiempo.
  • En este ejemplo, se resolvieron todas las dependencias, al menos con la marca --nobuild.
  • Es importante ejecutar la compilación completa (comando especificado) y corregir manualmente los posibles errores (p.ej., la cadena de herramientas no se registró correctamente).
  • El archivo migration_info.md contiene detalles sobre la migración. Consulta los detalles en esta sección.

Transformaciones

En esta sección, se ilustra la migración del código del archivo WORKSPACE a MODULE.bazel.

WORKSPACE - Bazel Module

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 de Bazel

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

WORKSPACE - Extensión de 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 - Extensión de 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: extensión de 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 - Extensión de 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: extensión de 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 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: regla de 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: Extensión del 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)

Sugerencias para la depuración

En esta sección, se proporcionan comandos e información útiles para depurar los problemas que pueden surgir durante la migración de Bzlmod.

Sugerencias útiles

  • Anular la versión: No es raro que la actualización de la versión de una dependencia cause problemas. Bzlmod podría cambiar la versión de la dependencia debido al algoritmo de MVS. Para usar la misma versión o una similar a la que se usaba en WORKSPACE, reemplázala con single_version_override. Ten en cuenta que esto es útil para depurar diferencias entre WORKSPACE y Bzlmod, pero no debes depender de esta función a largo plazo.

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

  • Usa el comando bazel mod.

    • Verifica la versión de un repo especificado con el comando show_repo. Por ejemplo:

      bazel mod show_repo @rules_python

    • Verifica la información sobre una extensión de módulo con el comando show_extension. Por ejemplo:

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

  • Usa el modo de proveedor para crear una copia local de un repo cuando quieras supervisar o controlar la fuente del repo. Por ejemplo:

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

Generación de informes de migración

Este archivo se actualiza con cada ejecución de la secuencia de comandos de migración o se genera desde cero si es la primera ejecución o si se usa la marca --i. El informe contiene la siguiente información:

  • Comando para pruebas locales.
  • Lista de dependencias directas (al menos las que se usan directamente en el proyecto).
  • Para cada dependencia, un menú desplegable para verificar dónde se declaró el repositorio en el archivo WORKSPACE, lo que es particularmente útil para la depuración. Puedes verlo de la siguiente manera:

    > Click here to see where and how the repo was declared in the WORKSPACE
    file
  • Para cada dependencia, cómo se implementó en el archivo MODULE.bazel. En el ejemplo de migración anterior, se vería de la siguiente manera:

    1. Dependencia del módulo de 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")`
      • La secuencia de comandos usará automáticamente el perfect name match si lo encuentra. En caso de error, puedes verificar si el nombre se agregó correctamente.
    2. Extensión de 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. Extensión de Maven: Migration of org.antlr (px_deps):

      maven.artifact(
          name = "px_deps",
          group = "org.antlr",
          artifact = "antlr4",
          version = "4.11.1"
      )
    4. Extensión 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",
          ],
      )
      • Se introdujo como un módulo Go con la ayuda de go.mod. Si go.mod y go.sum no están disponibles, el módulo de Go se agrega directamente al archivo MODULE.bazel.
      • gazelle_override se usa para agregar directivas específicas.

Comentarios

Si quieres contribuir, crea un problema o una PR en bazel-central-registry.