Migración a plataformas

Informar un problema Ver fuente

Bazel tiene compatibilidad sofisticada con plataformas de modelado y cadenas de herramientas para compilaciones multiarquitectónicas y compiladas de forma cruzada.

En esta página, se resume el estado de la asistencia.

Consulta lo siguiente:

Estado

C++

Las reglas de C++ usan plataformas para seleccionar cadenas de herramientas cuando se configura --incompatible_enable_cc_toolchain_resolution.

Esto significa que puedes configurar un proyecto de C++ con lo siguiente:

bazel build //:my_cpp_project --platforms=//:myplatform

en lugar de la heredada:

bazel build //:my_cpp_project` --cpu=... --crosstool_top=...  --compiler=...

Esta opción estará habilitada de forma predeterminada en Bazel 7.0 (#7260).

Para probar tu proyecto de C++ con plataformas, consulta Cómo migrar tu proyecto y Cómo configurar cadenas de herramientas de C++.

Java

Las reglas de Java usan plataformas para seleccionar cadenas de herramientas.

Esto reemplaza las marcas heredadas --java_toolchain, --host_java_toolchain, --javabase y --host_javabase.

Consulta Java y Bazel para obtener más información.

Android

Las reglas de Android usan plataformas para seleccionar cadenas de herramientas cuando se configura --incompatible_enable_android_toolchain_resolution.

Esto significa que puedes configurar un proyecto de Android con lo siguiente:

bazel build //:my_android_project --android_platforms=//:my_android_platform

en lugar de usar marcas heredadas, como --android_crosstool_top, --android_cpu y --fat_apk_cpu.

Esta opción estará habilitada de forma predeterminada en Bazel 7.0 (#16285).

Para probar tu proyecto de Android con plataformas, consulta Cómo migrar tu proyecto.

Una manzana

Las reglas de Apple no son compatibles con plataformas y aún no están programadas para su asistencia.

Sin embargo, aún puedes usar las APIs de la plataforma con compilaciones de Apple (por ejemplo, cuando compilas con una combinación de reglas de Apple y C++ puro) con asignaciones de plataformas.

Otros idiomas

  • Las reglas de Go son totalmente compatibles con las plataformas
  • Las reglas de Rust son totalmente compatibles con las plataformas.

Si tienes un conjunto de reglas de idioma, consulta Cómo migrar tu conjunto de reglas para agregar compatibilidad.

Información general

Se introdujeron las plataformas y las cadenas de herramientas para estandarizar la forma en que los proyectos de software se orientan a diferentes arquitecturas y se hacen compilaciones de forma cruzada.

Esto se inspiró en la observación de que los encargados de mantener el lenguaje ya lo hacían de forma ad hoc e incompatible. Por ejemplo, las reglas de C++ usaban --cpu y --crosstool_top para declarar una CPU y una cadena de herramientas de destino. Ninguna de estas opciones modela correctamente una "plataforma". Esto produjo compilaciones incorrectas y extrañas.

Java, Android y otros lenguajes desarrollaron sus propias marcas para fines similares, ninguno de los cuales interoperaba entre sí. Esto hizo que las compilaciones entre lenguajes sean confusas y complicadas.

Bazel está diseñado para proyectos grandes, multiplataforma y con varios lenguajes. Esto exige una compatibilidad de principios para estos conceptos, incluida una API estándar clara.

Necesidad de migración

La actualización a la nueva API requiere dos esfuerzos: liberar la API y actualizar la lógica de la regla para usarla.

La primera está lista, pero la segunda es continua. Esto consiste en garantizar que las plataformas y cadenas de herramientas específicas del lenguaje estén definidas, la lógica del lenguaje las lee a través de la API nueva en lugar de las marcas anteriores, como --crosstool_top, y las config_setting seleccionan la API nueva en lugar de las anteriores.

Este trabajo es sencillo, pero requiere un esfuerzo distinto para cada idioma, además de una advertencia justa para que los propietarios de proyectos prueben los próximos cambios.

Por este motivo, se trata de una migración en curso.

Objetivo

Esta migración se completa cuando todos los proyectos se compilan con el siguiente formato:

bazel build //:myproject --platforms=//:myplatform

Esto implica lo siguiente:

  1. Las reglas de tu proyecto eligen las cadenas de herramientas correctas para //:myplatform.
  2. Las dependencias de tu proyecto eligen las cadenas de herramientas correctas para //:myplatform.
  3. //:myplatform hace referencia a declaraciones comunes de CPU, OS y otras propiedades genéricas independientes del lenguaje.
  4. Todos los select() relevantes coinciden correctamente con //:myplatform.
  5. //:myplatform se define en un lugar claro y accesible, es decir, en el repositorio de tu proyecto si la plataforma es única para este o en algún lugar común donde todos los proyectos consumidores la puedan encontrar.

Las marcas antiguas, como --cpu, --crosstool_top y --fat_apk_cpu, dejarán de estar disponibles y se quitarán en cuanto sea seguro hacerlo.

En última instancia, esta será la única forma de configurar arquitecturas.

Migra tu proyecto

Si compilas con lenguajes que admiten plataformas, tu compilación ya debería funcionar con una invocación como la siguiente:

bazel build //:myproject --platforms=//:myplatform

Consulta el estado y la documentación de tu lenguaje para obtener información detallada.

Si un lenguaje requiere una marca para habilitar la compatibilidad con la plataforma, también debes configurarla. Consulta el estado para obtener más información.

Para compilar tu proyecto, debes verificar lo siguiente:

  1. Debe existir //:myplatform. Por lo general, es responsabilidad del propietario del proyecto definir las plataformas, ya que los distintos proyectos se orientan a distintas máquinas. Consulta Plataformas predeterminadas.

  2. Las cadenas de herramientas que quieres usar deben existir. Si usas cadenas de herramientas de acciones, los propietarios del idioma deben incluir instrucciones para registrarlas. Si escribes tus propias cadenas de herramientas personalizadas, debes register en tu archivo MODULE.bazel o con --extra_toolchains.

  3. Las select() y las transiciones de configuración deben resolverse de forma correcta. Consulta select() y Transiciones.

  4. Si tu compilación combina lenguajes que admiten plataformas y las que no, es posible que necesites asignaciones de plataformas para ayudar a los lenguajes heredados a funcionar con la nueva API. Consulta Asignaciones de plataformas para obtener más detalles.

Si los problemas persisten, comunícate para obtener asistencia.

Plataformas predeterminadas

Los propietarios del proyecto deben definir plataformas explícitas para describir las arquitecturas para las que quieren compilar. Luego, estos se activan con --platforms.

Cuando no se configura --platforms, Bazel usa de forma predeterminada un elemento platform que representa la máquina de compilación local. Esto se genera de forma automática en @platforms//host (con alias como @bazel_tools//tools:host_platform), por lo que no es necesario definirlo de forma explícita. Asigna el OS y la CPU de la máquina local con constraint_value declarados en @platforms.

select()

Los proyectos pueden select() en destinos constraint_value, pero no en plataformas completas. Esto es intencional, por lo que select() admite la mayor variedad de máquinas posible. Una biblioteca con fuentes específicas de ARM debería admitir todas las máquinas con tecnología ARM, a menos que haya un motivo para ser más específico.

Para seleccionar uno o más objetos constraint_value, usa lo siguiente:

config_setting(
    name = "is_arm",
    constraint_values = [
        "@platforms//cpu:arm",
    ],
)

Esto equivale a seleccionar tradicionalmente en --cpu:

config_setting(
    name = "is_arm",
    values = {
        "cpu": "arm",
    },
)

Obtén más detalles aquí.

Los select en --cpu, --crosstool_top, etc. no entienden --platforms. Cuando migras tu proyecto a plataformas, debes convertirlos a constraint_values o usar asignaciones de plataformas para admitir ambos diseños durante la migración.

Transiciones

Las transiciones de Starlark cambian las partes del gráfico de compilación. Si tu proyecto usa una transición que establece --cpu, --crossstool_top o cualquier otra marca heredada, las reglas que lean --platforms no verán estos cambios.

Cuando migras tu proyecto a plataformas, debes convertir cambios como return { "//command_line_option:cpu": "arm" } en return { "//command_line_option:platforms": "//:my_arm_platform" } o usar asignaciones de plataforma para admitir ambos diseños durante la migración.

Migra tu conjunto de reglas

Si tienes un conjunto de reglas y deseas admitir plataformas, debes hacer lo siguiente:

  1. Haz que la lógica de reglas resuelva cadenas de herramientas con la API de cadenas de herramientas. Consulta la API de la cadena de herramientas (ctx.toolchains).

  2. Opcional: Define una marca --incompatible_enable_platforms_for_my_language para que la lógica de reglas resuelva de manera alternativa las cadenas de herramientas a través de la nueva API o marcas antiguas, como --crosstool_top, durante las pruebas de migración.

  3. Define las propiedades relevantes que conforman los componentes de la plataforma. Consulta Propiedades comunes de plataformas

  4. Define cadenas de herramientas estándar y haz que los usuarios puedan acceder a ellas mediante las instrucciones de registro de tu regla (detalles).

  5. Asegúrate de que los select() y las transiciones de configuración sean compatibles con las plataformas. Ese es el mayor desafío. Es particularmente difícil para proyectos con varios idiomas (que pueden fallar si todos los idiomas no pueden leer --platforms).

Si necesitas combinar reglas que no admiten plataformas, es posible que necesites asignaciones de plataformas para cerrar la brecha.

Propiedades comunes de la plataforma

Las propiedades comunes de plataformas en varios lenguajes, como OS y CPU, se deben declarar en @platforms. De esta manera, se fomenta el uso compartido, la estandarización y la compatibilidad entre idiomas.

Las propiedades únicas de tus reglas deben declararse en el repositorio de tu regla. Esto te permite mantener una propiedad clara sobre los conceptos específicos de los que son responsables tus reglas.

Si tus reglas usan SO o CPU de propósito personalizado, debes declararlas en el repositorio de tu regla en lugar de @platforms.

Asignaciones de plataformas

Asignaciones de plataforma es una API temporal que permite que la lógica compatible con la plataforma se combine con la lógica heredada en la misma compilación. Esta es una herramienta simple cuyo único propósito es resolver las incompatibilidades con diferentes períodos de migración.

Una asignación de plataforma es un mapa de un platform() a un conjunto correspondiente de marcas heredadas o viceversa. Por ejemplo:

platforms:
  # Maps "--platforms=//platforms:ios" to "--ios_multi_cpus=x86_64 --apple_platform_type=ios".
  //platforms:ios
    --ios_multi_cpus=x86_64
    --apple_platform_type=ios

flags:
  # Maps "--ios_multi_cpus=x86_64 --apple_platform_type=ios" to "--platforms=//platforms:ios".
  --ios_multi_cpus=x86_64
  --apple_platform_type=ios
    //platforms:ios

  # Maps "--cpu=darwin_x86_64 --apple_platform_type=macos" to "//platform:macos".
  --cpu=darwin_x86_64
  --apple_platform_type=macos
    //platforms:macos

Bazel usa esto para garantizar que todas las opciones de configuración, tanto las basadas en la plataforma como las heredadas, se apliquen de manera coherente en toda la compilación, incluso a través de las transiciones.

De forma predeterminada, Bazel lee las asignaciones del archivo platform_mappings en la raíz de tu lugar de trabajo. También puedes configurar --platform_mappings=//:my_custom_mapping.

Consulta el diseño de asignaciones de plataformas para obtener más detalles.

Revisión de la API

Un objeto platform es una colección de destinos constraint_value:

platform(
    name = "myplatform",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:arm",
    ],
)

Una constraint_value es una propiedad de máquina. Los valores del mismo "tipo" se agrupan en un constraint_setting común:

constraint_setting(name = "os")
constraint_value(
    name = "linux",
    constraint_setting = ":os",
)
constraint_value(
    name = "mac",
    constraint_setting = ":os",
)

Un toolchain es una regla de Starlark. Sus atributos declaran las herramientas de un lenguaje (como compiler = "//mytoolchain:custom_gcc"). Sus proveedores pasan esta información a reglas que necesitan compilar con estas herramientas.

Las cadenas de herramientas declaran los constraint_value de las máquinas a las que pueden apuntar (target_compatible_with = ["@platforms//os:linux"]) y las máquinas en las que pueden ejecutarse sus herramientas (exec_compatible_with = ["@platforms//os:mac"]).

Cuando compilas $ bazel build //:myproject --platforms=//:myplatform, Bazel selecciona automáticamente una cadena de herramientas que puede ejecutarse en la máquina de compilación y compilar objetos binarios para //:myplatform. Esto se conoce como resolución de la cadena de herramientas.

El conjunto de cadenas de herramientas disponibles se puede registrar en el archivo MODULE.bazel con register_toolchains o en la línea de comandos con --extra_toolchains.

Obtén más información aquí.

Preguntas

Para obtener asistencia general o preguntas sobre el cronograma de migración, comunícate con bazel-discuss o los propietarios de las reglas adecuadas.

Para ver debates sobre el diseño y la evolución de las APIs de la plataforma o la cadena de herramientas, comunícate con bazel-dev.

Consulta también