Compila con plataformas

Informar un problema Ver fuente

Bazel ofrece una compatibilidad sofisticada para el modelado de plataformas y cadenas de herramientas. La integración de esto en proyectos reales requiere una cooperación cuidadosa entre los propietarios del código, los encargados de mantener 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 ningún lugar hasta que se actualicen todas las reglas de lenguaje, select() y otras referencias heredadas. Realizamos este proceso con frecuencia. Eventualmente, 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:

Información general

Se introdujeron las plataformas y las cadenas de herramientas para estandarizar cómo los proyectos de software se orientan a 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 de mantenimiento de lenguaje ya hacían esto de maneras ad hoc e incompatibles. Por ejemplo, las reglas de C++ usan --cpu y --crosstool_top para configurar la CPU de destino y la cadena de herramientas de C++ de la compilación. Ninguno de estos modelos modela correctamente una "plataforma". Los intentos históricos de hacerlo causaron compilaciones 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 multilingües y multiplataforma. Esto requiere una compatibilidad más basada en principios para estos conceptos, incluidas APIs claras que fomentan la interoperabilidad de lenguajes y proyectos. Para esto se usan estas nuevas APIs.

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 admitirlos. Esto requiere una secuencia de migración cuidadosa para mantener todos los proyectos y sus dependencias funcionando de forma correcta.

Por ejemplo, las reglas de C++ de Bazel admiten plataformas. Sin embargo, las reglas de Apple no lo hacen. Es posible que Apple no le interese Apple a tu proyecto de C++. Sin embargo, otros pueden hacerlo. Por lo tanto, aún no es seguro habilitar plataformas globalmente 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 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. Los proyectos que dependen del tuyo admiten //:myplatform o tu proyecto admite las APIs heredadas (como --crosstool_top).
  4. //:myplatform hace referencia a [declaraciones comunes][Declaración de plataforma común]{: .external} de CPU, OS y otros conceptos genéricos que admiten la compatibilidad automática entre proyectos.
  5. Todos los select() de los proyectos relevantes comprenden las propiedades de la máquina implícitas por //:myplatform.
  6. //:myplatform se define en un lugar claro y reutilizable: en el repositorio de tu proyecto, si la plataforma es única, de lo contrario, en algún lugar donde todos los proyectos que puedan usar esta plataforma puedan encontrar.

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

¿Debería usar plataformas?

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

Si eres responsable de mantener proyectos, lenguajes o cadenas de herramientas, con el tiempo querrás admitir las APIs nuevas. La espera hasta que se complete la migración global o la aceptación anticipada depende de tus necesidades específicas de valor o 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 CPU pueden admitir el mismo conjunto de instrucciones.
  • Compilaciones más adecuadas. Si realizas una operación 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 sencilla Todos los proyectos comprenden lo siguiente: --platforms=//:myplatform. No se necesitan 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, usarlas 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.
  • Para hacerlos funcionar, es posible que se requiera mantenimiento temporal adicional.
  • La coexistencia de APIs nuevas y heredadas requiere una guía más cuidadosa para el usuario para evitar confusiones.
  • Las definiciones canónicas para las propiedades comunes, como OS y CPU, aún están en evolución y pueden requerir contribuciones iniciales adicionales.
  • Las definiciones canónicas para las 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 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 detalles aquí.

Estado

La compatibilidad actual de la plataforma varía según los idiomas. Todas las principales reglas de Bazel se están moviendo a las plataformas. Pero este proceso llevará tiempo. Esto se debe a tres razones principales:

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

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

    Las definiciones de la plataforma también son necesarias (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. Los proyectos existentes deben migrarse. Las select() y las transiciones también deben migrarse. Este es el mayor desafío. Es particularmente desafiante para los proyectos con varios idiomas (que pueden fallar si todos los idiomas no pueden leer --platforms).

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

Propiedades comunes de la plataforma

Las propiedades de 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 entre 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 de "activa" con criterios incompatibles.

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

Plataformas predeterminadas

En general, los propietarios del proyecto deben definir plataformas explícitas para describir los tipos de máquinas 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.

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 la heredada:

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

Si tu proyecto es puro C++ y no dependen de proyectos que no son de C++, puedes usar plataformas de forma segura, siempre que tus select y transiciones sean compatibles. Consulta #7260 y Configura 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 que 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 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 problema n.o 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 está en camino.

Una manzana

Las reglas de Bazel de Apple aún no son compatibles con plataformas para seleccionar cadenas de herramientas de Apple.

Tampoco admiten dependencias de C++ habilitadas para la plataforma porque usan el --crosstool_top heredado para configurar la cadena de herramientas de C++. Hasta que se migren, puedes mezclar proyectos de Apple con C++ habilitado para platrm y asignaciones de plataforma (ejemplo).

Otros idiomas

Si estás diseñando 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 una buena explicación.

select()

Los proyectos pueden select() en destinos constraint_value, pero no en todas las plataformas. Esto es intencional para que los objetos select() admitan 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 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 en la ventana de 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 en la ventana de migración.

Cómo usar plataformas hoy en día

Si solo deseas crear o compilar de manera cruzada un proyecto, debes seguir la documentación oficial del proyecto. Los encargados de mantenimiento del lenguaje y del proyecto son los que determinan cómo y cuándo realizar la integración en las plataformas y qué valor ofrece.

Si eres responsable de mantenimiento de proyectos, lenguajes o cadenas 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 idiomas 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 algunos costos de mantenimiento (tienes que 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 mediante la asignación de la configuración de estilo --cpu a las plataformas correspondientes, y viceversa.

Asignaciones de plataformas

Las asignaciones de plataforma son una API temporal que permite que coexistan la lógica con tecnología de la plataforma y la lógica heredada en la misma compilación a través del 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 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.

Consulta aquí para obtener todos los detalles.

Preguntas

Para obtener asistencia general y 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/cadena de herramientas, comunícate con bazel-dev@googlegroups.com.

Consulta también