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 enlazadores 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 con nombre de opciones para estas restricciones, que representa los recursos particulares que están disponibles en algún 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 en combinación con la 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.
- Execution: Es una plataforma en la que las herramientas de compilación ejecutan acciones de compilación para producir resultados intermedios y finales.
- Target : Es una plataforma en la que reside y se ejecuta un resultado final.
Bazel admite las siguientes situaciones de compilación en relación con las plataformas:
Compilaciones de una sola plataforma (predeterminadas): Las plataformas de host, ejecución y destino son las mismas. 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 ejecuta en una MacBook Pro.
Compilaciones de varias plataformas : Las plataformas de host, ejecución y destino son todas diferentes.
Cómo definir restricciones y plataformas
El espacio de opciones posibles para las plataformas se define con las
constraint_setting y las
constraint_value reglas 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 efectiva una enumeración y sus valores posibles. Por ejemplo, el siguiente
fragmento de un archivo BUILD presenta 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 espacio 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 mediante la definición de tu propio valor.
La regla platform introduce 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 de 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 generalmente ú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. Todos se encuentran en https://github.com/bazelbuild/platforms.
Bazel se entrega con la siguiente definición de plataforma especial:
@local_config_platform//:host. Este es el valor de la plataforma de host detectada automáticamente -
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 de host y destino para una compilación con las siguientes marcas de línea de comandos:
--host_platform: El valor predeterminado es@bazel_tools//platforms:host_platform.--platforms: El valor predeterminado es@bazel_tools//platforms:target_platform.
Cómo omitir destinos incompatibles
Cuando se compila para una plataforma de destino específica, suele ser conveniente omitir
los destinos que nunca funcionarán en esa plataforma. Por ejemplo, es probable que el controlador de dispositivos de Windows genere muchos errores del compilador cuando se compile en una
máquina Linux con //.... Usa el
target_compatible_with
atributo para indicarle a Bazel qué restricciones de plataforma de destino tiene tu código.
El uso más simple de este atributo restringe un destino 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 es solo compatible con la compilación con Windows de 64 bits e
incompatible con todo lo demás. La incompatibilidad es transitiva. Cualquier destino
que dependa de forma transitiva de un destino incompatible también se considera
incompatible.
¿Cuándo se omiten los destinos?
Los destinos se omiten cuando se consideran incompatibles y se incluyen en la compilación como parte de una expansión de patrón 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
Las pruebas incompatibles en un test_suite se
omiten de manera similar si se especifica test_suite en la línea de comandos con
--expand_test_suites.
En otras palabras, los destinos test_suite en la línea de comandos se comportan como :all y
.... El uso de --noexpand_test_suites impide la expansión y hace que
test_suite destinos 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 fallará la compilación.
$ 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
@platforms//:incompatible
constraint_value que ninguna plataforma
satisface.
Usa select() en combinación con
@platforms//:incompatible para expresar restricciones más complicadas. Por
ejemplo, úsalo para implementar la lógica OR básica. A continuación, se 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 orienta 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. Como@platforms//:incompatibleno forma parte de ninguna plataforma, el destino se considera incompatible.
Para que tus restricciones sean más legibles, usa
skylib's
selects.with_or().
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 el
IncompatiblePlatformProvider
en bazel cquery's formato de salida de Starlark para distinguir
los destinos incompatibles de los compatibles.
Esto se puede usar para filtrar destinos 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 destinos incompatibles ignoran las restricciones de visibilidad.