O Bazel pode criar e testar códigos 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 variar, como arquitetura de CPU, presença ou ausência de uma GPU ou 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 que estão 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 config_setting para gravar atributos configuráveis.
O Bazel reconhece três papéis que uma plataforma pode atender:
- 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 oferece suporte aos seguintes cenários de build em relação a plataformas:
Builds de plataforma única (padrão): as plataformas de host, execução e 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 de host e execução são as mesmas, mas a plataforma segmentada é diferente. Por exemplo, criar um app iOS no macOS executado em um MacBook Pro.
Builds de várias plataformas : as plataformas de host, execução e destino são todas diferentes.
Como definir restrições e plataformas
O espaço de opções possíveis para plataformas é definido usando as
constraint_setting e as
constraint_value regras em arquivos BUILD. constraint_setting cria uma nova dimensão, enquanto constraint_value cria um novo valor para uma determinada dimensão. Juntos, eles definem um enum e os valores possíveis. 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 delas 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 usuais. Se a visibilidade permitir, você poderá estender uma configuração de restrição atual definindo seu próprio valor.
A regra platform apresenta uma nova plataforma com
determinadas opções de valores de restrição. O exemplo 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 glibc de 2.25. Consulte 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 populares. Todos eles estão localizados em https://github.com/bazelbuild/platforms (link em inglês).
O Bazel é fornecido com a seguinte definição de plataforma especial: @local_config_platform//:host. Esse é o valor da plataforma de host detectada 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 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
Como ignorar destinos incompatíveis
Ao criar para uma plataforma segmentada específica, geralmente é recomendável ignorar destinos que nunca funcionarão nessa plataforma. Por exemplo, é provável que o driver de dispositivo do Windows gere muitos erros de compilador ao criar em uma máquina Linux com //.... Use o
target_compatible_with
atributo para informar ao Bazel quais restrições de plataforma segmentada 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",
],
)
:win_driver_lib é apenas compatível com a criação do 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 os destinos são ignorados?
Os destinos são ignorados quando são considerados incompatíveis e incluídos no build como parte de uma expansão de padrão de destino. Por exemplo, as duas invocações a seguir ignoram todos os destinos incompatíveis encontrados em uma expansão de padrão de destino.
$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all
Os testes incompatíveis em um test_suite também são
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
test_suite destinos com testes incompatíveis também sejam incompatíveis.
A especificação explícita de um destino incompatível na linha de comando resulta em uma mensagem de erro e um 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
atende.
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. O exemplo a seguir marca 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 exemplo acima pode ser interpretado da seguinte maneira:
- 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 tem a restrição
@platforms//:incompatible. Como@platforms//:incompatiblenão faz parte de nenhuma plataforma, o destino é considerado incompatível.
Para tornar as restrições mais legíveis, use
skylib's
selects.with_or().
É possível 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": [],
],
)
Como detectar destinos incompatíveis usando bazel cquery
É possível usar o
IncompatiblePlatformProvider
no formato de saída do bazel cquery's Starlark para distinguir destinos incompatíveis de compatíveis.
Isso pode ser usado para filtrar destinos incompatíveis. O exemplo abaixo só vai imprimir 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.