Plataformas

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

Bazel puede compilar y probar código en una variedad de hardware, sistemas operativos y configuraciones del sistema, con muchas versiones diferentes de herramientas de compilación, como vinculadores y compiladores. Para ayudar a administrar esta complejidad, Bazel tiene un concepto de restricciones y plataformas. Una restricción es una dimensión en la que los entornos de compilación o producción pueden diferir, como la arquitectura de la CPU, la presencia o ausencia de una GPU, o la versión de un compilador instalado en el sistema. Una plataforma es una colección de opciones con nombre para estas restricciones que representa los recursos específicos que están disponibles en un entorno.

Modelar el entorno como una plataforma ayuda a Bazel a seleccionar automáticamente las cadenas de herramientas adecuadas para las acciones de compilación. Las plataformas también se pueden usar junto con la regla config_setting para escribir atributos configurables.

Bazel reconoce tres roles que puede cumplir una plataforma:

  • Host: Es la plataforma en la que se ejecuta Bazel.
  • Ejecución: Es una plataforma en la que las herramientas de compilación ejecutan acciones de compilación para produzir resultados intermedios y finales.
  • Destino: Es una plataforma en la que reside y se ejecuta un resultado final.

Bazel admite las siguientes situaciones de compilación relacionadas con plataformas:

  • Compilaciones de una sola plataforma (predeterminado): Las plataformas de host, ejecución y destino son las mismas. Por ejemplo, compilar un ejecutable de Linux en Ubuntu que se ejecute en una CPU Intel x64.

  • Compilaciones de compilación cruzada: Las plataformas de host y de ejecución son las mismas, pero la plataforma de destino es diferente. Por ejemplo, compilar una app para iOS en macOS que se ejecuta en una MacBook Pro.

  • Compilaciones multiplataforma: Las plataformas de host, ejecución y destino son diferentes.

Definición de restricciones y plataformas

El espacio de opciones posibles para las plataformas se define mediante las reglas constraint_setting y constraint_value dentro de los archivos BUILD. constraint_setting crea una dimensión nueva, mientras que constraint_value crea un valor nuevo para una dimensión determinada. Juntos, definen de manera eficaz una enumeración y sus valores posibles. Por ejemplo, el siguiente fragmento de un archivo BUILD introduce una restricción para la versión de glibc del sistema con dos valores posibles.

constraint_setting(name = "glibc_version")

constraint_value(
    name = "glibc_2_25",
    constraint_setting = ":glibc_version",
)

constraint_value(
    name = "glibc_2_26",
    constraint_setting = ":glibc_version",
)

Las restricciones y sus valores se pueden definir en diferentes paquetes del lugar de trabajo. Se hace referencia a ellos por etiqueta y están sujetos a los controles de visibilidad habituales. Si la visibilidad lo permite, puedes extender una configuración de restricción existente si defines tu propio valor para ella.

La regla platform presenta una plataforma nueva con ciertas opciones de valores de restricción. A continuación, se crea una plataforma llamada linux_x86 y se indica que describe cualquier entorno que ejecute un sistema operativo Linux en una arquitectura x86_64 con una versión glibc 2.25. (Consulta a continuación para obtener más información sobre las restricciones integradas de Bazel).

platform(
    name = "linux_x86",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
        ":glibc_2_25",
    ],
)

Restricciones y plataformas que suelen ser útiles

Para mantener la coherencia del ecosistema, el equipo de Bazel mantiene un repositorio con definiciones de restricciones para las arquitecturas de CPU y los sistemas operativos más populares. Estos se encuentran en https://github.com/bazelbuild/platforms.

Bazel se envía con la siguiente definición de plataforma especial: @local_config_platform//:host. Este es el valor de la plataforma de host detectado automáticamente: representa la plataforma detectada para el sistema en el que se ejecuta Bazel.

Cómo especificar una plataforma para una compilación

Puedes especificar el host y las plataformas de destino de una compilación con las siguientes marcas de línea de comandos:

  • --host_platform: El valor predeterminado es @local_config_platform//:host
    • @local_config_platform es una regla de repositorio que detecta el SO y la CPU del host y escribe el destino de la plataforma.
    • También crea @local_config_platform//:constraintz.bzl, que expone un array llamado HOST_CONSTRAINTS, que se puede usar en otros archivos de BUILD y Starlark.
  • --platforms: El valor predeterminado es la plataforma host
    • Esto significa que cuando no se establecen otras marcas, @local_config_platform//:host es la plataforma de destino.
    • Si se establece --host_platform y no --platforms, el valor de --host_platform es la plataforma de destino y el host.

Omisión de objetivos incompatibles

Cuando compilas para una plataforma de destino específica, a menudo es conveniente omitir los destinos que nunca funcionarán en esa plataforma. Por ejemplo, es probable que el controlador de tu dispositivo con Windows genere muchos errores del compilador cuando se compila en una máquina Linux con //.... Usa el atributo target_compatible_with para indicarle a Bazel qué restricciones de plataforma de destino tiene tu código.

El uso más sencillo de este atributo restringe un objetivo a una sola plataforma. El destino no se compilará para ninguna plataforma que no satisfaga todas las restricciones. En el siguiente ejemplo, se restringe win_driver_lib.cc a Windows de 64 bits.

cc_library(
    name = "win_driver_lib",
    srcs = ["win_driver_lib.cc"],
    target_compatible_with = [
        "@platforms//cpu:x86_64",
        "@platforms//os:windows",
    ],
)

:win_driver_lib solo es compatible para compilar en Windows de 64 bits y no es compatible con todo lo demás. La incompatibilidad es transitiva. Todos los objetivos que dependen transitivamente de un objetivo incompatible se consideran incompatibles.

¿Cuándo se omiten los objetivos?

Se omiten los destinos cuando se consideran incompatibles y se incluyen en la compilación como parte de una expansión de patrones de destino. Por ejemplo, las dos invocaciones siguientes omiten los destinos incompatibles que se encuentran en una expansión de patrón de destino.

$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all

Las pruebas incompatibles en un objeto test_suite se omiten de forma similar si se especifica test_suite en la línea de comandos con --expand_test_suites. En otras palabras, los objetivos test_suite en la línea de comandos se comportan como :all y .... El uso de --noexpand_test_suites evita la expansión y hace que los objetivos test_suite con pruebas incompatibles también sean incompatibles.

Si especificas de forma explícita un destino incompatible en la línea de comandos, se generará un mensaje de error y la compilación fallará.

$ bazel build --platforms=//:myplatform //:target_incompatible_with_myplatform
...
ERROR: Target //:target_incompatible_with_myplatform is incompatible and cannot be built, but was explicitly requested.
...
FAILED: Build did NOT complete successfully

Los objetivos explícitos incompatibles se omiten de forma silenciosa si --skip_incompatible_explicit_targets está habilitado.

Restricciones más expresivas

Para obtener más flexibilidad a la hora de expresar restricciones, usa el objeto @platforms//:incompatible constraint_value que ninguna plataforma satisface.

Usa select() junto con @platforms//:incompatible para expresar restricciones más complicadas. Por ejemplo, úsalo para implementar lógica OR básica. La siguiente marca es una biblioteca compatible con macOS y Linux, pero no con otras plataformas.

cc_library(
    name = "unixish_lib",
    srcs = ["unixish_lib.cc"],
    target_compatible_with = select({
        "@platforms//os:osx": [],
        "@platforms//os:linux": [],
        "//conditions:default": ["@platforms//:incompatible"],
    }),
)

Lo anterior se puede interpretar de la siguiente manera:

  1. Cuando se segmenta para macOS, el objetivo no tiene restricciones.
  2. Cuando se segmenta para Linux, el objetivo no tiene restricciones.
  3. De lo contrario, el objetivo tiene la restricción @platforms//:incompatible. Dado que @platforms//:incompatible no forma parte de ninguna plataforma, el destino se considera incompatible.

Para que tus restricciones sean más legibles, usa selects.with_or() de skylib.

Puedes expresar la compatibilidad inversa de manera similar. En el siguiente ejemplo, se describe una biblioteca que es compatible con todo excepto ARM.

cc_library(
    name = "non_arm_lib",
    srcs = ["non_arm_lib.cc"],
    target_compatible_with = select({
        "@platforms//cpu:arm": ["@platforms//:incompatible"],
        "//conditions:default": [],
    }),
)

Cómo detectar destinos incompatibles con bazel cquery

Puedes usar IncompatiblePlatformProvider en el formato de salida de Starlark de bazel cquery para distinguir destinos incompatibles de los compatibles.

Esto se puede usar para filtrar objetivos incompatibles. En el siguiente ejemplo, solo se imprimirán las etiquetas de los objetivos que sean compatibles. No se imprimen los objetivos incompatibles.

$ cat example.cquery

def format(target):
  if "IncompatiblePlatformProvider" not in providers(target):
    return target.label
  return ""


$ bazel cquery //... --output=starlark --starlark:file=example.cquery

Problemas conocidos

Los destinos incompatibles ignoran las restricciones de visibilidad.