Compila con plataformas

Bazel tiene compatibilidad sofisticada para modelar plataformas y cadenas de herramientas. Para integrar esto con proyectos reales, se requiere una cooperación cuidadosa entre los propietarios del código, los encargados del mantenimiento de 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 plataforma y 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. Sigue leyendo para ver dónde encajan tus compilaciones.

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

Fondo

Se introdujeron plataformas y cadenas de herramientas para estandarizar la forma en que los proyectos de software segmentan diferentes máquinas y compilan con las herramientas de lenguaje adecuadas.

Esta es una incorporación relativamente reciente a Bazel. Se inspiró en la observación de que los encargados del mantenimiento del lenguaje ya lo hacían de formas ad hoc e incompatibles. Por ejemplo, las reglas de C++ usan --cpu y --crosstool_top para establecer la CPU de destino y la cadena de herramientas de C++ de una compilación. Ninguno de estos modela correctamente una "plataforma". Los intentos históricos de hacerlo causaron compilaciones incómodas e inexactas. 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, multilingües y multiplataforma. Esto exige una compatibilidad más basada en principios para estos conceptos, incluidas APIs claras que fomenten la interoperabilidad de lenguajes y proyectos. Para eso son estas nuevas APIs.

Migración

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

Por ejemplo, las reglas de C++ de Bazel admiten plataformas. Pero las reglas de Apple no. Es posible que a tu proyecto de C++ no le interese Apple. Pero a otros sí. Por lo tanto, aún no es seguro habilitar plataformas de forma global 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 encajar tus proyectos.

Objetivo

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

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

Esto implica lo siguiente:

  1. Las reglas que usa tu proyecto pueden inferir cadenas de herramientas correctas de //:myplatform.
  2. Las reglas que usan las dependencias de tu proyecto pueden inferir cadenas de herramientas correctas de //:myplatform.
  3. O bien los proyectos que dependen del tuyo admiten //:myplatform o bien tu proyecto admite las APIs heredadas (como --crosstool_top).
  4. //:myplatform hace referencia a [declaraciones comunes][Common Platform Declaration]{: .external} de CPU, OS y otros conceptos genéricos que admiten la compatibilidad automática entre proyectos.
  5. Todos los select()s de los proyectos relevantes comprenden las propiedades de la 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 exclusiva de tu proyecto; de lo contrario, en algún lugar donde todos los proyectos que puedan usar esta plataforma puedan encontrarla.

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

¿Debo usar plataformas?

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

Si eres encargado del mantenimiento de un proyecto, un lenguaje o una cadena de herramientas, es posible que quieras admitir las APIs nuevas. Si esperas hasta que se complete la migración global o si participas antes, depende de tus necesidades específicas de valor o costo:

Valor

  • Puedes select() o elegir cadenas de herramientas en las propiedades exactas que te interesan en lugar de marcas codificadas, como --cpu. Por ejemplo, varias CPUs pueden admitir el mismo conjunto de instrucciones.
  • Compilaciones más correctas. Si select() con --cpu en el ejemplo anterior 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: --platforms=//:myplatform. No es necesario usar varias marcas específicas del 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 cadena de herramientas adecuada 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 el tuyo.
  • Para que funcionen, es posible que se requiera un mantenimiento temporal adicional.
  • La coexistencia de APIs nuevas y heredadas requiere una guía del usuario más cuidadosa para evitar confusiones.
  • Las definiciones canónicas para propiedades comunes como OS y CPU aún están en evolución y pueden requerir contribuciones iniciales adicionales.
  • Las definiciones canónicas para cadenas de herramientas específicas del lenguaje aún están en evolución y pueden requerir contribuciones iniciales adicionales.

Revisión de la API

Un platform es una colección de constraint_value destinos:

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

Un constraint_value es una propiedad de la 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 deben compilarse con estas herramientas.

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

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

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

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

Estado

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

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

  2. Los encargados del mantenimiento de la cadena de herramientas deben definir cadenas de herramientas y hacerlas accesibles a los usuarios (en repositorios de GitHub y entradas WORKSPACE). Esto es técnicamente sencillo, pero debe organizarse de forma inteligente para mantener una experiencia del usuario sencilla.

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

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

Si diseñas un nuevo conjunto de reglas, debes admitir plataformas desde el principio. Esto hace que tus reglas sean compatibles automáticamente con otras reglas y proyectos, con un valor cada vez mayor a medida que la API de la plataforma se vuelve más omnipresente.

Propiedades comunes de la plataforma

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

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

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

Plataformas predeterminadas

En general, los propietarios del proyecto deben definir plataformas explícitas para describir los tipos de máquinas para los que quieren compilar. Luego, se activan con --platforms.

Cuando no se establece --platforms, Bazel usa de forma predeterminada un platform que representa la máquina de compilación local. Esto se genera automáticamente en @local_config_platform//:host, por lo que no es necesario definirlo de forma explícita. Asigna OS y 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 de lo heredado:

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

Si tu proyecto es C++ puro y no depende de proyectos que no sean de C++, puedes usar plataformas de forma segura siempre que tus selects y transiciones sean compatibles. Consulta #7260 y Configura cadenas de herramientas de C++ para obtener más orientació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). Por lo tanto, esto depende de que las reglas de Apple migren 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 obtener información sobre cómo usar las marcas de configuración, consulta el manual de Bazel y Java. Para obtener más información, consulta el documento de diseño.

Si aún usas marcas heredadas, sigue el proceso de migración en el problema #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. Sin embargo, la migración está en camino.

Apple

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

Tampoco admiten dependencias de C++ habilitadas para la plataforma porque usan la --crosstool_top heredada para establecer la cadena de herramientas de C++. Hasta que se migre, puedes combinar proyectos de Apple con C++ habilitado para la plataforma con asignaciones de plataforma (ejemplo).

Otros idiomas

  • Las reglas de Rust de Bazel admiten plataformas por completo.
  • Las reglas de Go de Bazel admiten plataformas por completo (detalles).

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

select()

Los proyectos pueden select() en constraint_value destinos pero no en plataformas completas. Esto es intencional para que select() admita la mayor variedad posible de máquinas. Una biblioteca con fuentes específicas de ARM debe 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 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 comprenden --platforms. Cuando migres tu proyecto a plataformas, debes convertirlos en constraint_values o usar asignaciones de plataforma para admitir ambos estilos a través de la ventana de migración.

Transiciones

Las transiciones de Starlark cambian las marcas en partes de tu 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 leen --platforms no verán estos cambios.

Cuando migres tu proyecto a plataformas, debes convertir los cambios como return { "//command_line_option:cpu": "arm" } a return { "//command_line_option:platforms": "//:my_arm_platform" } o usar asignaciones de plataforma para admitir ambos estilos a través de la ventana de migración.

Cómo usar las plataformas hoy

Si solo quieres compilar o compilar de forma cruzada un proyecto, debes seguir la documentación oficial del proyecto. Depende de los encargados del mantenimiento del lenguaje y del proyecto determinar cómo y cuándo realizar la integración con las plataformas, y qué valor ofrece.

Si eres encargado del mantenimiento de un proyecto, un lenguaje o una 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 el parámetro "usar plataformas" para los lenguajes de tu proyecto (si tienen uno) y realiza las pruebas que necesites para ver si funcionan los proyectos que te interesan.

  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 un costo de mantenimiento (debes asegurarte de que la configuración coincida de forma manual). Sin embargo, esto debería funcionar en ausencia de transiciones renegadas .

  3. Escribe asignaciones de plataforma para admitir ambos estilos asignando la configuración de estilo --cpu a las plataformas correspondientes y viceversa.

Asignaciones de plataforma

Asignaciones de plataforma es una API temporal que permite que la lógica heredada y la lógica con tecnología de plataforma coexistan en la misma compilación a través de la ventana 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 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 las transiciones.

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

Consulta aquí para obtener todos los detalles.

Preguntas

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

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

Consulta también