Compila con plataformas

Informar un problema Ver código fuente Nightly · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Bazel tiene una compatibilidad sofisticada para modelar plataformas y cadenas de herramientas. La integración de esto con proyectos reales requiere cooperación cuidadosa entre los propietarios del código, los mantenedores de 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.

Resumen: 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, los 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

Las plataformas y las cadenas de herramientas se introdujeron para estandarizar la forma en que los proyectos de software se orientan a diferentes máquinas y se 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 representa correctamente una “plataforma”. Los intentos históricos de hacerlo provocaron compilaciones incómodas y poco precisas. 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 eso se crearon estas nuevas APIs.

Migración

Las APIs de la plataforma y la 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 los select() de un proyecto deben admitirlos. Esto requiere una secuencia de migración cuidadosa para mantener todos los proyectos y sus dependencias funcionando correctamente.

Por ejemplo, las reglas de C++ de Bazel admiten plataformas. Sin embargo, las reglas de Apple no lo hacen. Es posible que a tu proyecto de C++ no le importe Apple. 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 cadenas de herramientas correctas 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 quieres compilar o realizar una compilación cruzada de 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 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 correctas. Si select() con --cpu en el ejemplo anterior, y luego agregas una CPU nueva que admite el mismo conjunto de instrucciones, select() no reconoce 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 es necesario usar varias marcas específicas del idioma en la línea de comandos.
  • Diseño de lenguaje más simple. Todos los lenguajes comparten una API común para definir, usar y seleccionar la cadena de herramientas adecuada 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 que funcionen, es posible que se requiera mantenimiento temporal adicional.
  • La coexistencia de APIs nuevas y heredadas requiere una orientación al usuario más cuidadosa para evitar confusiones.
  • Las definiciones canónicas de las propiedades comunes, como OS y CPU, aún están evolucionando 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 objetivos constraint_value:

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 orientarse (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 se puede ejecutar en la máquina de compilación y compila 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.

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

Estado

La compatibilidad actual de la plataforma varía según el idioma. Todas las reglas principales de Bazel se están trasladando a las plataformas. Sin embargo, 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). Por lo 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 multilingües (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 creciente 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 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

Por lo general, los propietarios de proyectos deben definir plataformas explícitas para describir los tipos de máquinas para las que desean 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. 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 todavía configuran dependencias de C++ con --cpu y --crosstool_top (ejemplo). Por lo tanto, esto depende de las reglas de Apple que se migran 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 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 el --crosstool_top heredado para configurar 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

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

select()

Los proyectos pueden select() en objetivos constraint_value, 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 debería admitir todas las máquinas con ARM, a menos que haya motivos para ser más específicos.

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 de --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 partes de tu gráfico de compilación. Si tu proyecto usa una transición que establece --cpu, --crossstool_top o otras marcas heredadas, las reglas que leen --platforms no verán estos cambios.

Cuando migres tu proyecto a plataformas, debes convertir cambios como return { "//command_line_option:cpu": "arm" } a 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 quieres compilar o realizar una compilación cruzada de un proyecto, debes seguir la documentación oficial del proyecto. Depende de los mantenedores de lenguajes y proyectos determinar cómo y cuándo realizar la integración con las plataformas, y qué valor ofrece.

Si eres el encargado de mantener 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 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 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 plataformas para admitir ambos estilos asignando 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 la lógica basada en la plataforma y la basada en la versión heredada coexistan 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 hablar 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