Compila con plataformas

Informar un problema Ver fuente Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Bazel ofrece una compatibilidad sofisticada para el modelado de plataformas y cadenas de herramientas. La integración de esto en proyectos reales requiere 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. Con el tiempo, todas las compilaciones se basarán en la plataforma. Sigue leyendo para ver dónde se ajustan tus compilaciones.

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

Segundo plano

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 lenguajes ya lo hacían de formas incompatibles y ad hoc. Por ejemplo, las reglas de C++ usan --cpu y --crosstool_top para configurar la CPU y la cadena de herramientas de C++ de destino de una 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, multiplataforma y multilingües. Esto requiere una mayor compatibilidad con estos conceptos, incluidas APIs claras que fomenten 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. Es posible que Apple no le interese Apple a tu proyecto de C++. Sin embargo, es posible que otras sí lo hagan. 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 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 de //:myplatform.
  2. Las reglas que usan las dependencias de tu proyecto pueden inferir las cadenas de herramientas correctas a partir de //:myplatform.
  3. O bien los proyectos que dependen del tuyo admiten //:myplatform o tu proyecto admite las APIs heredadas (como --crosstool_top).
  4. //:myplatform hace referencia a [common declarations][Common Platform Declaration]{: .external} de CPU, OS y otros conceptos genéricos que admiten compatibilidad automática entre proyectos.
  5. Todos los select() 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 única para tu proyecto, de lo contrario, 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. Luego, esta será la forma estándar en que los proyectos seleccionarán plataformas y cadenas de herramientas.

¿Debo usar plataformas?

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

Si eres el encargado de mantener un proyecto, un lenguaje o un conjunto de herramientas, con el tiempo querrás admitir las nuevas APIs. Esperar hasta que se complete la migración global o habilitar la función con anticipación 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 CPUs 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 entienden: --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.
  • Se pueden omitir los destinos en la fase de compilación y prueba si son incompatibles 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 de 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 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 el idioma. 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. La lógica de reglas debe actualizarse para obtener información de la herramienta 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 mantener las cadenas de herramientas deben definirlas y permitir que los usuarios puedan acceder a ellas (en los repositorios de GitHub y las entradas de WORKSPACE). Esto es técnicamente sencillo, pero se debe organizar de forma 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). En general, los proyectos deben definir sus propias plataformas.

  3. Se deben migrar los proyectos existentes. También se deben migrar los select() y las transiciones. 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 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 "arm" con criterios incompatibles.

Las propiedades comunes a nivel global se declaran en el repositorio de @platforms (por lo que la etiqueta canónica del ejemplo anterior es @platforms//cpu:arm). Las propiedades comunes a nivel de idioma deben declararse 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. Se genera automáticamente en @local_config_platform//:host, por lo que no es necesario definirlo de forma explícita. 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 depende de proyectos que no sean de C++, puedes usar las plataformas de forma segura, siempre y cuando 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 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. Pero la migración está en camino.

Apple

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 select() admita la mayor variedad posible de máquinas. 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 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 información aquí.

Los select en --cpu, --crosstool_top, etc. no entienden --platforms. Cuando migres tu proyecto a plataformas, debes convertirlo 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 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 basados en la plataforma como heredados, se apliquen de forma coherente durante 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