Migración a plataformas

Informar un problema Ver fuente

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

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

Consulta lo siguiente:

Estado

C++

Las reglas de C++ usan plataformas para seleccionar cadenas de herramientas cuando se establece --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 las 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.

Para obtener más información, consulta Java y Bazel.

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 con 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 compatibilidad.

Puedes seguir usando 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 plataforma.

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 Migra 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 compilan de forma cruzada.

Esto se inspiró en la observación de que los encargados de mantenimiento de lenguaje ya lo hacían de formas incompatibles y ad hoc. Por ejemplo, las reglas de C++ usaban --cpu y --crosstool_top para declarar una CPU de destino y una cadena de herramientas. Ninguno de estos modelos modela correctamente una "plataforma". Esto generó compilaciones incorrectas e incómodas.

Java, Android y otros lenguajes desarrollaron sus propias marcas con fines similares, pero 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 multilingües y multiplataforma. Esto requiere una asistencia más basada en principios para estos conceptos, incluida una API estándar clara.

Necesidad de migración

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

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

Este trabajo es sencillo, pero requiere un esfuerzo distinto para cada lenguaje, además de advertencias adecuadas para que los propietarios de proyectos prueben los próximos cambios.

Por eso, esta es 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 elementos select() relevantes coinciden correctamente con //:myplatform.
  5. //:myplatform se define en un lugar claro y accesible: en el repositorio de tu proyecto, si la plataforma es única para este, o en algún lugar común donde todos los proyectos que consumen pueden encontrarla.

Las marcas anteriores, 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 manera de configurar las arquitecturas.

Cómo migrar 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 Estado y la documentación de tu lenguaje para obtener detalles precisos.

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

Para que tu proyecto se compile, debes verificar lo siguiente:

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

  2. Las cadenas de herramientas que deseas usar deben existir. Si usas cadenas de herramientas de archivo, los propietarios de lenguajes deben incluir instrucciones para registrarlos. Si escribes tus propias cadenas de herramientas personalizadas, debes register en WORKSPACE o con --extra_toolchains.

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

  4. Si tu compilación combina lenguajes que admiten y no plataformas, es posible que necesites asignaciones de plataforma para ayudar a que los idiomas heredados funcionen con la API nueva. 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 desean compilar. Luego, se activan con --platforms.

Cuando --platforms no está configurado, Bazel usa de forma predeterminada una platform que representa la máquina de compilación local. Este se genera automáticamente en @local_config_platform//:host, por lo que no es necesario definirlo explícitamente. Asigna el OS y el CPU de la máquina local con los constraint_value declarados en @platforms.

select()

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

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

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

Esto equivale a seleccionar de forma tradicional en --cpu:

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

Obtén más detalles aquí.

Los elementos select en --cpu, --crosstool_top, etc. no comprenden --platforms. Cuando migres tu proyecto a plataformas, debes convertirlos a constraint_values o usar asignaciones de plataforma para admitir ambos estilos durante la migración.

Transiciones

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

Cuando migres tu proyecto a plataformas, debes convertir los 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 ventana de migración.

Migra tu conjunto de reglas

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

  1. Haz que la lógica de las reglas resuelva las cadenas de herramientas con la API de la cadena 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 la regla resuelva de forma alternativa las cadenas de herramientas a través de la API nueva o a través de marcas antiguas como --crosstool_top durante las pruebas de migración.

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

  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. Este es el mayor desafío. Es particularmente desafiante para los proyectos de 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 plataforma para cerrar la brecha.

Propiedades comunes de la plataforma

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

Las propiedades exclusivas de tus reglas se deben declarar en el repositorio de estas. 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 con propósitos personalizados, debes declararlas en el repositorio de la regla en lugar de @platforms.

Asignaciones de plataformas

Las asignaciones de plataforma son una API temporal que permite que la lógica adaptada a la plataforma se mezcle con la lógica heredada en la misma compilación. Esta es una herramienta simple que solo está diseñada para suavizar 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 "--cpu=ios_x86_64 --apple_platform_type=ios".
  //platforms:ios
    --cpu=ios_x86_64
    --apple_platform_type=ios

flags:
  # Maps "--cpu=ios_x86_64 --apple_platform_type=ios" to "--platforms=//platforms:ios".
  --cpu=ios_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 todos los parámetros de configuración, tanto los basados en la plataforma como los heredados, se apliquen de manera coherente en toda la compilación, incluso a través de 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.

Para obtener más detalles, consulta el diseño de asignaciones de plataformas.

Revisión de la API

Un platform es una colección de destinos constraint_value:

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

Un constraint_value es una propiedad de máquina. Los valores del mismo "tipo" se agrupan bajo 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 idioma (como compiler = "//mytoolchain:custom_gcc"). Sus proveedores pasan esta información a las reglas que deben compilarse con estas herramientas.

Las cadenas de herramientas declaran los constraint_value de las máquinas a las que se pueden segmentar (target_compatible_with = ["@platforms//os:linux"]) y las máquinas en las que pueden ejecutarse (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 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 WORKSPACE 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 y preguntas sobre el cronograma de migración, comunícate con bazel- visual o con los propietarios de las reglas correspondientes.

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

Consulta también