Compila con plataformas

Bazel tiene compatibilidad sofisticada para modelar plataformas y cadenas de herramientas. La integración de esto con proyectos reales requiere una cooperación cuidadosa entre los propietarios de código, los encargados de mantener las reglas y los desarrolladores principales de Bazel.

En esta página, se resume el propósito de las plataformas y se muestra cómo compilar con ellas.

tl;dr: Las APIs de la plataforma y la cadena de herramientas de Bazel están disponibles, pero no funcionarán en todas partes hasta que se actualicen todas las reglas de lenguaje, select() y otras referencias heredadas. Realizamos este proceso con frecuencia. Con el tiempo, todas las compilaciones se basarán en la plataforma. Lee a continuación para ver dónde encajan tus compilaciones.

Para obtener documentación más formal, consulta lo siguiente:

Información general

Se introdujeron plataformas y cadenas de herramientas para estandarizar la manera en que los proyectos de software se orientan a diferentes máquinas y realizan compilaciones con las herramientas de lenguaje adecuadas.

Esta es una incorporación relativamente reciente de Bazel. Se inspiró en la observación de que los encargados de mantenimiento del lenguaje ya hacían esto de formas incompatibles y ad hoc. Por ejemplo, las reglas de C++ usan --cpu y --crosstool_top para configurar la CPU de destino de una compilación y la cadena de herramientas de C++. Ninguno de estos modela correctamente una "plataforma". Los intentos históricos de hacerlo causaron construcciones incómodas e imprecisas. Estas marcas tampoco controlan la compilación de Java, que desarrolló su propia interfaz independiente con --java_toolchain.

Bazel está diseñado para proyectos grandes, multiplataforma y multilingües. Esto requiere una mayor compatibilidad de principios para estos conceptos, incluidas las APIs claras que fomentan la interoperabilidad del proyecto y el lenguaje. Para eso están las APIs nuevas.

Migración

Las APIs de la plataforma y la cadena de herramientas solo funcionan cuando los proyectos realmente las usan. Esto no es trivial porque la lógica de reglas, las cadenas de herramientas, las dependencias y los select() de un proyecto deben ser compatibles. Esto requiere una secuencia de migración cuidadosa para que todos los proyectos y sus dependencias funcionen de forma correcta.

Por ejemplo, las reglas C++ de Bazel admiten plataformas. Pero las reglas de Apple no. Es posible que Apple no le importe a tu proyecto de C++. Sin embargo, otros pueden hacerlo. Por lo tanto, aún no es seguro habilitar globalmente plataformas para todas las compilaciones de C++.

En el resto de esta página, se describe esta secuencia de migración y cómo y cuándo pueden adaptarse tus proyectos.

Objetivo

La migración de la plataforma de Bazel se completa cuando todos los proyectos se compilan con el siguiente formato:

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

Esto implica lo siguiente:

  1. Las reglas que usa tu proyecto pueden inferir las cadenas de herramientas correctas a partir de //:myplatform.
  2. Las reglas que usan las dependencias de tu proyecto pueden inferir las cadenas de herramientas correctas a partir de //:myplatform.
  3. Ya sea los proyectos que dependan del tuyo admitan //:myplatform o tu proyecto admita las APIs heredadas (como --crosstool_top).
  4. //:myplatform hace referencia a [declaraciones comunes][Declaración común de la plataforma]{: .external} de CPU, OS y otros conceptos genéricos que admiten la compatibilidad automática entre proyectos.
  5. Todas las select() de proyectos relevantes comprenden las propiedades de máquina que implica //:myplatform.
  6. //:myplatform se define en un lugar claro y reutilizable: en el repositorio de tu proyecto si la plataforma es única para él o en algún lugar que puedan encontrar todos los proyectos que puedan usar esta plataforma.

Las APIs anteriores se quitarán en cuanto se alcance este objetivo. Esta será la forma estándar en que los proyectos seleccionan plataformas y cadenas de herramientas.

¿Debo usar plataformas?

Si solo deseas compilar o compilar de forma cruzada un proyecto, debes seguir la documentación oficial del proyecto.

Si eres un encargado de mantenimiento de proyectos, lenguajes o cadenas de herramientas, con el tiempo, deberás admitir las nuevas APIs. El hecho de que esperes hasta que se complete la migración global o la habilites antes de tiempo dependerá de tus necesidades específicas de valor / costo:

Valor

  • Puedes usar select() o elegir cadenas de herramientas en las propiedades exactas que te interesan en lugar de marcas hard-coded como --cpu. Por ejemplo, varias CPUs pueden admitir el mismo conjunto de instrucciones.
  • Más compilaciones correctas. Si en el ejemplo anterior realizaste select() con --cpu y, luego, agregas una CPU nueva que admita el mismo conjunto de instrucciones, select() no reconocerá la CPU nueva. Sin embargo, un select() en las plataformas sigue siendo preciso.
  • Experiencia del usuario más simple. Todos los proyectos comprenden lo siguiente: --platforms=//:myplatform. No es necesario usar varias marcas específicas de lenguaje en la línea de comandos.
  • Diseño de lenguaje más simple. Todos los lenguajes comparten una API común para definir cadenas de herramientas, usar cadenas de herramientas y seleccionar la correcta para una plataforma.
  • Los destinos se pueden omitir en la fase de compilación y prueba si no son compatibles con la plataforma de destino.

Costos

  • Es posible que los proyectos dependientes que aún no admiten plataformas no funcionen automáticamente con los tuyos.
  • Hacerlos funcionar puede requerir mantenimiento temporal adicional.
  • La coexistencia de APIs nuevas y heredadas requiere una orientación más detallada del usuario para evitar confusiones.
  • Las definiciones canónicas de las propiedades comunes, como OS y CPU, siguen evolucionando y pueden requerir contribuciones iniciales adicionales.
  • Las definiciones canónicas de las cadenas de herramientas específicas del lenguaje aún están evolucionando y pueden requerir contribuciones iniciales adicionales.

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",
    ],
)

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 las reglas que necesitan compilar con estas herramientas.

Las cadenas de herramientas declaran los constraint_value de las máquinas a las que se pueden orientar (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 objetos binarios de compilación 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.

Consulta aquí para obtener información más detallada.

Estado

La compatibilidad de la plataforma actual varía según los idiomas. Todas las reglas principales de Bazel se trasladan a las plataformas. Sin embargo, este proceso tomará tiempo. Esto se debe a tres motivos principales:

  1. Se debe actualizar la lógica de reglas para obtener información de las herramientas de la nueva API de la cadena de herramientas (ctx.toolchains) y dejar de leer la configuración heredada, como --cpu y --crosstool_top. Esto es relativamente sencillo.

  2. Los encargados de mantenimiento de las cadenas de herramientas deben definirlas y hacer que los usuarios puedan acceder a ellas (en repositorios de GitHub y entradas de WORKSPACE). Esto es técnicamente sencillo, pero debe organizarse de manera inteligente para mantener una experiencia del usuario sencilla.

    Las definiciones de plataforma también son necesarias (a menos que compiles para la misma máquina en la que se ejecuta Bazel). Generalmente, los proyectos deben definir sus propias plataformas.

  3. Se deben migrar los proyectos existentes. También se deben migrar las select() y las transiciones. Este es el mayor desafío. Es particularmente desafiante para proyectos con varios idiomas (que pueden fallar si todos los idiomas no pueden leer --platforms).

Si vas a diseñar un nuevo conjunto de reglas, debes admitir las plataformas desde el principio. Esto hace que tus reglas sean compatibles con otras reglas y proyectos automáticamente, y aumenta el valor a medida que la API de la plataforma se vuelve más universal.

Propiedades comunes de la plataforma

Las propiedades de la plataforma, como OS y CPU, que son comunes en todos los proyectos, se deben declarar en un lugar estándar y centralizado. Esto fomenta la compatibilidad entre proyectos y entre idiomas.

Por ejemplo, si MyApp tiene un select() en constraint_value @myapp//cpus:arm y SomeCommonLib tiene un select() en @commonlib//constraints:arm, se activarán sus modos "activar" con criterios incompatibles.

Las propiedades globales comunes se declaran en el repositorio @platforms (por lo que la etiqueta canónica del ejemplo anterior es @platforms//cpu:arm). Las propiedades comunes del lenguaje deben declararse en los repositorios de sus respectivos idiomas.

Plataformas predeterminadas

Por lo general, los propietarios de proyectos deben definir plataformas explícitas para describir los tipos de máquinas para las que quieren compilar. Luego, estos se activan con --platforms.

Cuando no se configura --platforms, Bazel usa de forma predeterminada una platform que representa la máquina de compilación local. Se genera de forma automática en @local_config_platform//:host, 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.

C++

Las reglas de C++ de Bazel usan plataformas para seleccionar cadenas de herramientas cuando configuras --incompatible_enable_cc_toolchain_resolution (#7260).

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

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

en lugar del heredado:

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

Si tu proyecto es de C++ puro y no dependen de proyectos que no sean de C++, puedes usar plataformas de forma segura siempre que tus select y transiciones sean compatibles. Consulta #7260 y Cómo configurar cadenas de herramientas de C++ para obtener más información.

Este modo no está habilitado de forma predeterminada. Esto se debe a que los proyectos de Apple aún configuran dependencias de C++ con --cpu y --crosstool_top (ejemplo). Esto depende de las reglas de Apple en la migración a las plataformas.

Java

Las reglas de Java de Bazel usan plataformas.

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

Para aprender a usar las marcas de configuración, consulta el manual de Bazel y Java. Para obtener información adicional, consulta el documento de diseño.

Si aún usas marcas heredadas, sigue el proceso de migración en el error 7849.

Android

Las reglas de Android de Bazel usan plataformas para seleccionar cadenas de herramientas cuando configuras --incompatible_enable_android_toolchain_resolution.

Esta opción no está habilitada de forma predeterminada. Pero la migración va en camino.

Una manzana

Las reglas de Apple de Bazel aún no admiten plataformas para seleccionar cadenas de herramientas de Apple.

Tampoco son compatibles con las dependencias de C++ habilitadas para la plataforma porque usan --crosstool_top heredado para configurar la cadena de herramientas de C++. Hasta que se complete la migración, puedes combinar proyectos de Apple con C++ habilitado para platillos con asignaciones de plataformas (ejemplo).

Otros idiomas

Si diseñas reglas para un lenguaje nuevo, usa las plataformas para seleccionar las cadenas de herramientas de tu lenguaje. Consulta la documentación sobre cadenas de herramientas para obtener una buena explicación.

select()

Los proyectos pueden select() en destinos constraint_value, pero no en plataformas completas. Esto es intencional para que select() admita una variedad más amplia de máquinas como sea posible. Una biblioteca con fuentes específicas de ARM debe admitir todas las máquinas con tecnología ARM, a menos que se deba ser más específica.

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 la selección 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 entienden --platforms. Cuando migras tu proyecto a plataformas, debes convertirlos a constraint_values o usar asignaciones de plataformas para admitir ambos estilos durante la ventana de migración.

Transiciones

Las transiciones de Starlark cambian las marcas de 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 los cambios como return { "//command_line_option:cpu": "arm" } en return { "//command_line_option:platforms": "//:my_arm_platform" } o usar asignaciones de plataformas para admitir ambos estilos durante la ventana de migración.

Cómo usar las plataformas en la actualidad

Si solo deseas compilar o compilar de forma cruzada un proyecto, debes seguir la documentación oficial del proyecto. Depende de los encargados de mantener el lenguaje y los proyectos determinar cómo y cuándo integrar las plataformas, y qué valor ofrece.

Si eres un encargado del proyecto, lenguaje o cadena de herramientas y tu compilación no usa plataformas de forma predeterminada, tienes tres opciones (además de esperar la migración global):

  1. Activa la marca “Usar plataformas” para los lenguajes de tu proyecto (si tienen una) y realiza las pruebas que necesites para ver si los proyectos que te interesan funcionan.

  2. Si los proyectos que te interesan aún dependen de marcas heredadas, como --cpu y --crosstool_top, úsalas junto con --platforms:

    bazel build //:my_mixed_project --platforms==//:myplatform --cpu=... --crosstool_top=...
    

    Esto tiene algo de costo de mantenimiento (debes asegurarte de que la configuración coincida de forma manual). Pero esto debería funcionar en ausencia de transiciones renegadas.

  3. Escribe asignaciones de plataformas para admitir ambos estilos asignando configuraciones de estilo --cpu a las plataformas correspondientes y viceversa.

Asignaciones de plataformas

Las asignaciones de plataforma son una API temporal que permite que la lógica basada en la plataforma y la lógica heredada coexistan en la misma compilación durante el período de baja de esta última.

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 --apple_platform_type=macos" to "//platform:macos".
  --cpu=darwin
  --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 aquí para obtener más información.

Preguntas

Si necesitas asistencia general o tienes preguntas sobre el cronograma de migración, comunícate con bazel-discuss@googlegroups.com o con los propietarios de las reglas correspondientes.

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

Consulta también