O Bazel pode criar e testar código em vários hardwares, sistemas operacionais e configurações de sistema, usando muitas versões diferentes de ferramentas de build, como linkers e compiladores. Para ajudar a gerenciar essa complexidade, o Bazel tem um conceito de restrições e plataformas. Uma restrição é uma dimensão em que os ambientes de build ou produção podem ser diferentes, como arquitetura da CPU, presença ou ausência de uma GPU ou a versão de um compilador instalado no sistema. Uma plataforma é uma coleção nomeada de opções para essas restrições, representando os recursos específicos disponíveis em algum ambiente.
A modelagem do ambiente como uma plataforma ajuda o Bazel a selecionar automaticamente os conjuntos de ferramentas adequados para ações de build. As plataformas também podem ser usadas em combinação com a regra config_setting para gravar atributos configuráveis.
O Bazel reconhece três funções que uma plataforma pode desempenhar:
- Host: a plataforma em que o próprio Bazel é executado.
- Execução: uma plataforma em que as ferramentas de build executam ações de build para produzir saídas intermediárias e finais.
- Destino: uma plataforma em que uma saída final reside e é executada.
O Bazel é compatível com os seguintes cenários de build em relação a plataformas:
Builds de plataforma única (padrão): as plataformas host, de execução e de destino são as mesmas. Por exemplo, criar um executável do Linux no Ubuntu executado em uma CPU Intel x64.
Builds de compilação cruzada: as plataformas host e de execução são as mesmas, mas a plataforma de destino é diferente. Por exemplo, criar um app iOS no macOS em um MacBook Pro.
Builds multiplataforma: as plataformas de host, execução e destino são todas diferentes.
Como definir restrições e plataformas
O espaço de possíveis escolhas para plataformas é definido usando as regras constraint_setting
e constraint_value
em arquivos BUILD
. constraint_setting
cria uma dimensão, enquanto constraint_value
cria um valor para uma determinada dimensão. Juntos, eles definem um enum e seus possíveis valores. Por exemplo, o snippet a seguir de um arquivo BUILD
apresenta uma restrição para a versão glibc do sistema com dois valores possíveis.
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",
)
As restrições e os valores podem ser definidos em diferentes pacotes no espaço de trabalho. Eles são referenciados por rótulo e estão sujeitos aos controles de visibilidade comuns. Se a visibilidade permitir, você poderá estender uma configuração de restrição atual definindo seu próprio valor para ela.
A regra platform
apresenta uma nova plataforma com
determinadas opções de valores de restrição. O
comando a seguir cria uma plataforma chamada linux_x86
e informa que ela descreve qualquer
ambiente que execute um sistema operacional Linux em uma arquitetura x86_64 com uma
versão 2.25 da glibc. Confira abaixo mais informações sobre as restrições integradas do Bazel.
platform(
name = "linux_x86",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
":glibc_2_25",
],
)
Restrições e plataformas geralmente úteis
Para manter o ecossistema consistente, a equipe do Bazel mantém um repositório com definições de restrição para as arquiteturas de CPU e sistemas operacionais mais usados. Todos estão localizados em https://github.com/bazelbuild/platforms.
O Bazel é fornecido com a seguinte definição especial de plataforma:
@local_config_platform//:host
. Esse é o valor da plataforma do host detectado automaticamente, que representa a plataforma detectada automaticamente para o sistema em que o Bazel está sendo executado.
Como especificar uma plataforma para um build
É possível especificar as plataformas de host e de destino para um build usando as seguintes flags de linha de comando:
--host_platform
: o padrão é@bazel_tools//platforms:host_platform
.--platforms
: o padrão é@bazel_tools//platforms:target_platform
.
Ignorando destinos incompatíveis
Ao criar para uma plataforma de destino específica, muitas vezes é recomendável ignorar
destinos que nunca funcionarão nessa plataforma. Por exemplo, o driver do dispositivo Windows provavelmente vai gerar muitos erros de compilador ao criar em uma máquina Linux com //...
. Use o atributo
target_compatible_with
para informar ao Bazel quais restrições de plataforma de destino seu código tem.
O uso mais simples desse atributo restringe um destino a uma única plataforma.
O destino não será criado para nenhuma plataforma que não atenda a todas as restrições. O exemplo a seguir restringe win_driver_lib.cc
ao 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",
],
)
O :win_driver_lib
é apenas compatível com a criação no Windows de 64 bits e
incompatível com todo o resto. A incompatibilidade é transitiva. Todos os destinos que dependem transitivamente de um destino incompatível também são considerados incompatíveis.
Quando as metas são ignoradas?
As metas são ignoradas quando são consideradas incompatíveis e incluídas no build como parte de uma expansão de padrão de destino. Por exemplo, as duas invocações a seguir ignoram destinos incompatíveis encontrados em uma expansão de padrão de destino.
$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all
Testes incompatíveis em um test_suite
são
igualmente ignorados se o test_suite
for especificado na linha de comando com
--expand_test_suites
.
Em outras palavras, os destinos test_suite
na linha de comando se comportam como :all
e ...
. O uso de --noexpand_test_suites
impede a expansão e faz com que
os destinos test_suite
com testes incompatíveis também sejam incompatíveis.
Especificar explicitamente um destino incompatível na linha de comando resulta em uma mensagem de erro e uma build com falha.
$ 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
Restrições mais expressivas
Para mais flexibilidade na expressão de restrições, use o
@platforms//:incompatible
constraint_value
que nenhuma plataforma
satisfaz.
Use select()
em combinação com
@platforms//:incompatible
para expressar restrições mais complicadas. Por exemplo, use-o para implementar a lógica OR básica. A marcação a seguir indica uma biblioteca
compatível com macOS e Linux, mas não com outras plataformas.
cc_library(
name = "unixish_lib",
srcs = ["unixish_lib.cc"],
target_compatible_with = select({
"@platforms//os:osx": [],
"@platforms//os:linux": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)
O texto acima pode ser interpretado da seguinte forma:
- Ao segmentar o macOS, o destino não tem restrições.
- Ao segmentar o Linux, o destino não tem restrições.
- Caso contrário, o destino terá a restrição
@platforms//:incompatible
. Como@platforms//:incompatible
não faz parte de nenhuma plataforma, o destino é considerado incompatível.
Para tornar suas restrições mais legíveis, use
selects.with_or()
da
skylib.
Você pode expressar a compatibilidade inversa de maneira semelhante. O exemplo a seguir descreve uma biblioteca compatível com tudo exceto ARM.
cc_library(
name = "non_arm_lib",
srcs = ["non_arm_lib.cc"],
target_compatible_with = select({
"@platforms//cpu:arm": ["@platforms//:incompatible"],
"//conditions:default": [],
],
)
Detectar destinos incompatíveis usando bazel cquery
Use o
IncompatiblePlatformProvider
no formato de saída do Starlark
do bazel cquery
para distinguir
destinos incompatíveis de compatíveis.
Isso pode ser usado para filtrar destinos incompatíveis. O exemplo abaixo vai imprimir apenas os rótulos dos destinos compatíveis. Os destinos incompatíveis não são impressos.
$ 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 conhecidos
Os destinos incompatíveis ignoram as restrições de visibilidade.