Bazel puede compilar y probar código en una variedad de hardware, sistemas operativos y configuraciones de sistemas 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 por 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 algunos entornos.
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 en combinación con la regla config_setting para escribir atributos configurables.
Bazel reconoce tres roles que puede entregar una plataforma:
- Host: La plataforma en la que se ejecuta Bazel.
- Ejecución: Una plataforma en la que las herramientas de compilación ejecutan acciones de compilación para producir resultados intermedios y finales.
- Destino: Es una plataforma en la que se encuentra y ejecuta un resultado final.
Bazel admite las siguientes situaciones de compilación con respecto a las plataformas:
Compilaciones de plataforma única (predeterminado): El host, la ejecución y las plataformas de destino son iguales. Por ejemplo, compilar un ejecutable de Linux en Ubuntu que se ejecuta en una CPU Intel x64.
Compilaciones de compilación cruzada: Las plataformas de host y ejecución son las mismas, pero la plataforma de destino es diferente. Por ejemplo, compilar una app para iOS en macOS que se ejecute en una MacBook Pro.
Compilaciones multiplataforma: Las plataformas de host, ejecución y destino son diferentes.
Cómo definir restricciones y plataformas
El espacio de opciones posibles para 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. Juntas definen de manera efectiva una enum y sus posibles valores. Por ejemplo, el siguiente fragmento de un archivo BUILD
introduce una restricción para la versión 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 en el lugar de trabajo. Se hace referencia a ellas por etiqueta y están sujetas a los controles de visibilidad habituales. Si la visibilidad lo permite, puedes extender una configuración de restricciones 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 describe cualquier entorno que ejecute un sistema operativo Linux en una arquitectura x86_64 con una versión glibc de 2.25. (A continuación, encontrarás 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
A fin de 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. Todos 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, que representa la plataforma detectada automáticamente para el sistema en el que se ejecuta Bazel.
Cómo especificar una plataforma para una compilación
Puedes especificar las plataformas host y de destino para una compilación mediante las siguientes marcas de línea de comandos:
--host_platform
(la configuración predeterminada es@bazel_tools//platforms:host_platform
)--platforms
(la configuración predeterminada es@bazel_tools//platforms:target_platform
)
Cómo omitir objetivos incompatibles
Cuando se compila para una plataforma de destino específica, a menudo se recomienda omitir objetivos que nunca funcionarán en esa plataforma. Por ejemplo, es probable que el controlador de tu dispositivo Windows genere muchos errores del compilador cuando realices compilaciones en una máquina Linux con //...
. Usa el atributo
target_compatible_with
para indicarle a Bazel las restricciones de la plataforma de destino que tiene tu código.
El uso más sencillo de este atributo restringe un segmento a una sola plataforma.
El destino no se compilará para ninguna plataforma que no cumpla con 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 con la compilación en Windows de 64 bits y no es compatible con todo lo demás. La incompatibilidad es transitiva. Todos los objetivos que dependen de manera transitiva de un destino incompatible se consideran incompatibles a sí mismos.
¿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 siguientes dos invocaciones omiten cualquier destino incompatible que se encuentre en una expansión de patrón de destino.
$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all
De manera similar, las pruebas incompatibles en una test_suite
se omiten si test_suite
se especifica en la línea de comandos con --expand_test_suites
.
En otras palabras, los objetivos de 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 lo sean.
La especificación explícita de un destino incompatible en la línea de comandos da como resultado un mensaje de error y una compilación con errores.
$ 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
Restricciones más expresivas
Para obtener más flexibilidad en la expresión de 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 la lógica OR básica. Lo siguiente marca 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:
- Cuando se segmenta a macOS, el destino no tiene restricciones.
- Cuando se orienta a Linux, el destino no tiene restricciones.
- De lo contrario, el destino tiene la restricción
@platforms//:incompatible
. Debido a que@platforms//:incompatible
no forma parte de ninguna plataforma, el destino se considera incompatible.
Para que las restricciones sean más legibles, usa selects.with_or()
de skylib.
Puedes expresar la compatibilidad inversa de una 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": [],
],
)
Detecta destinos incompatibles con bazel cquery
Puedes usar IncompatiblePlatformProvider
en el formato de salida de Starlark de bazel cquery
para distinguir los destinos incompatibles de los compatibles.
Esto se puede usar para filtrar los objetivos incompatibles. En el siguiente ejemplo, solo se imprimirán las etiquetas de los destinos compatibles. No se imprimen los destinos 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 objetivos incompatibles ignoran las restricciones de visibilidad.