O Bazel tem suporte sofisticado para modelagem de plataformas e toolchains. A integração com projetos reais exige cooperação cuidadosa entre proprietários de código, mantenedores de regras e desenvolvedores principais do Bazel.
Esta página resume a finalidade das plataformas e mostra como criar com elas.
Resumo:as APIs de plataforma e cadeia de ferramentas do Bazel estão disponíveis, mas não vão funcionar
em todos os lugares até que todas as regras de linguagem, select()
s e outras referências legadas
sejam atualizadas. Esse trabalho é constante. Eventualmente, todos os builds serão baseados em plataforma.
Leia abaixo para saber onde suas builds se encaixam.
Para documentação mais formal, consulte:
Contexto
As plataformas e os toolchains foram introduzidos para padronizar a forma como os projetos de software segmentam diferentes máquinas e criam com as ferramentas de linguagem certas.
Essa é uma adição relativamente recente ao Bazel. Ele foi inspirado na observação de que os mantenedores de idiomas já faziam isso de maneiras incompatíveis e ad hoc. Por exemplo, as regras do C++ usam --cpu
e --crosstool_top
para definir a CPU de destino e o conjunto de ferramentas do C++ de um build. Nenhum deles modela corretamente uma "plataforma". Tentativas históricas de fazer isso resultaram em builds estranhas e imprecisas.
Essas flags também não controlam a compilação do Java, que desenvolveu uma interface independente com --java_toolchain
.
O Bazel é destinado a projetos grandes, multilíngues e multiplataforma. Isso exige um suporte mais fundamentado para esses conceitos, incluindo APIs claras que incentivam a interoperabilidade de linguagem e projeto. É para isso que servem essas novas APIs.
Migração
As APIs de plataforma e cadeia de ferramentas só funcionam quando os projetos as usam. Isso não é trivial porque a lógica de regras, as toolchains, as dependências e os
select()
s de um projeto precisam oferecer suporte a eles. Isso exige uma sequência de migração cuidadosa
para manter todos os projetos e dependências funcionando corretamente.
Por exemplo, as regras C++ do Bazel são compatíveis com plataformas. Mas as Regras da Apple não. Seu projeto em C++ pode não se importar com a Apple. Mas outras podem. Portanto, ainda não é seguro ativar globalmente as plataformas para todos os builds em C++.
O restante desta página descreve essa sequência de migração e como e quando seus projetos podem se encaixar.
Meta
A migração da plataforma do Bazel será concluída quando todos os projetos forem criados com o formulário:
bazel build //:myproject --platforms=//:myplatform
Isso implica:
- As regras usadas pelo projeto podem inferir conjuntos de ferramentas corretos de
//:myplatform
. - As regras usadas pelas dependências do projeto podem inferir conjuntos de ferramentas corretos de
//:myplatform
. - Os projetos dependem do seu para oferecer suporte a
//:myplatform
ou seu projeto oferece suporte às APIs legadas (como--crosstool_top
). //:myplatform
faz referência a [declarações comuns][Common Platform Declaration]{: .external} deCPU
,OS
e outros conceitos genéricos que oferecem suporte à compatibilidade automática entre projetos.- Todos os
select()
s dos projetos relevantes entendem as propriedades da máquina implícitas por//:myplatform
. //:myplatform
é definido em um lugar claro e reutilizável: no repositório do projeto se a plataforma for exclusiva dele. Caso contrário, em algum lugar que todos os projetos que podem usar essa plataforma possam encontrar.
As APIs antigas serão removidas assim que essa meta for alcançada. Essa será a maneira padrão de os projetos selecionarem plataformas e cadeias de ferramentas.
Devo usar plataformas?
Se você só quiser criar ou fazer a compilação cruzada de um projeto, siga a documentação oficial dele.
Se você for um mantenedor de projeto, linguagem ou cadeia de ferramentas, vai querer oferecer suporte às novas APIs. Aguardar a conclusão da migração global ou ativar a opção antecipadamente depende das suas necessidades específicas de valor / custo:
Valor
- Você pode
select()
ou escolher toolchains nas propriedades exatas que você quer, em vez de flags codificadas, como--cpu
. Por exemplo, várias CPUs podem ser compatíveis com o mesmo conjunto de instruções. - Mais builds corretas. Se você
select()
com--cpu
no exemplo acima e adicionar uma nova CPU que seja compatível com o mesmo conjunto de instruções, oselect()
não vai reconhecer a nova CPU. Mas umselect()
em plataformas continua preciso. - Experiência do usuário mais simples. Todos os projetos entendem:
--platforms=//:myplatform
. Não é necessário usar várias flags específicas de idioma na linha de comando. - Design de linguagem mais simples. Todas as linguagens compartilham uma API comum para definir cadeias de ferramentas, usar cadeias de ferramentas e selecionar a cadeia de ferramentas certa para uma plataforma.
- Os destinos podem ser ignorados na fase de build e teste se forem incompatíveis com a plataforma de destino.
Custos
- Projetos dependentes que ainda não oferecem suporte a plataformas podem não funcionar automaticamente com o seu.
- Para que eles funcionem, talvez seja necessário fazer uma manutenção temporária adicional.
- A coexistência de APIs novas e legadas exige uma orientação mais cuidadosa para evitar confusão.
- As definições canônicas para propriedades comuns, como
OS
eCPU
, ainda estão evoluindo e podem exigir contribuições iniciais extras. - As definições canônicas para toolchains específicas de linguagem ainda estão evoluindo e podem exigir contribuições iniciais extras.
Revisão da API
Um platform
é um conjunto de constraint_value
segmentações:
platform(
name = "myplatform",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:arm",
],
)
Um constraint_value
é uma propriedade da máquina. Valores do mesmo "tipo" são agrupados em um constraint_setting
comum:
constraint_setting(name = "os")
constraint_value(
name = "linux",
constraint_setting = ":os",
)
constraint_value(
name = "mac",
constraint_setting = ":os",
)
Um toolchain
é uma regra do Starlark. Os atributos dele declaram as ferramentas de uma linguagem (como compiler =
"//mytoolchain:custom_gcc"
). Os provedores transmitem essas informações para regras que precisam ser criadas com essas ferramentas.
As toolchains declaram os constraint_value
s das máquinas que podem segmentar (target_compatible_with = ["@platforms//os:linux"]
) e as máquinas em que as ferramentas podem ser executadas (exec_compatible_with = ["@platforms//os:mac"]
).
Ao criar $ bazel build //:myproject --platforms=//:myplatform
, o Bazel
seleciona automaticamente uma cadeia de ferramentas que pode ser executada na máquina de build e
criar binários para //:myplatform
. Isso é conhecido como resolução da cadeia de ferramentas.
O conjunto de toolchains disponíveis pode ser registrado no WORKSPACE
com
register_toolchains
ou na
linha de comando com --extra_toolchains
.
Saiba mais aqui.
Status
A compatibilidade atual de plataformas varia de acordo com o idioma. Todas as principais regras do Bazel estão sendo movidas para plataformas. Mas esse processo vai levar tempo. Isso acontece por três motivos principais:
A lógica da regra precisa ser atualizada para receber informações da ferramenta da nova API toolchain (
ctx.toolchains
) e parar de ler configurações legadas, como--cpu
e--crosstool_top
. Isso é relativamente simples.Os mantenedores de toolchains precisam definir e disponibilizar as toolchains para os usuários (em repositórios do GitHub e entradas
WORKSPACE
). Isso é tecnicamente simples, mas precisa ser organizado de forma inteligente para manter uma experiência do usuário fácil.As definições de plataforma também são necessárias, a menos que você crie para a mesma máquina em que o Bazel é executado. Em geral, os projetos definem as próprias plataformas.
Os projetos atuais precisam ser migrados.
select()
s e transições também precisam ser migrados. Esse é o maior desafio. Isso é especialmente difícil para projetos multilíngues, que podem falhar se todos os idiomas não puderem ler--platforms
.
Se você estiver criando um novo conjunto de regras, precisará oferecer suporte a plataformas desde o início. Isso torna automaticamente suas regras compatíveis com outras regras e projetos, aumentando o valor à medida que a API da plataforma se torna mais abrangente.
Propriedades comuns da plataforma
Propriedades da plataforma, como OS
e CPU
, que são comuns em todos os projetos precisam ser declaradas em um local padrão e centralizado. Isso incentiva a compatibilidade entre projetos e linguagens.
Por exemplo, se MyApp tiver um select()
em constraint_value
@myapp//cpus:arm
e SomeCommonLib tiver um select()
em
@commonlib//constraints:arm
, eles vão acionar os modos "arm" com critérios
incompatíveis.
As propriedades comuns globais são declaradas no repositório
@platforms
(portanto, o rótulo canônico do exemplo acima é @platforms//cpu:arm
).
As propriedades comuns a um idioma precisam ser declaradas nos repositórios dos respectivos
idiomas.
Plataformas padrão
Em geral, os proprietários de projetos precisam definir plataformas explícitas para descrever os tipos de máquinas que querem criar. Elas são acionadas com
--platforms
.
Quando --platforms
não está definido, o Bazel usa um platform
padrão que representa a máquina de build local. Ele é gerado automaticamente em @local_config_platform//:host
, então não é necessário defini-lo explicitamente. Ele mapeia o OS
e o CPU
da máquina local com constraint_value
s declarados em @platforms
.
C++
As regras de C++ do Bazel usam plataformas para selecionar toolchains quando você define
--incompatible_enable_cc_toolchain_resolution
(#7260).
Isso significa que é possível configurar um projeto em C++ com:
bazel build //:my_cpp_project --platforms=//:myplatform
em vez do legado:
bazel build //:my_cpp_project` --cpu=... --crosstool_top=... --compiler=...
Se o projeto for C++ puro e não depender de projetos que não sejam C++, você poderá usar
plataformas com segurança, desde que os select
s e as
transições sejam compatíveis. Consulte
#7260 e
Como configurar toolchains C++ para mais orientações.
Esse modo não é ativado por padrão. Isso acontece porque os projetos da Apple
ainda configuram dependências de C++ com --cpu
e --crosstool_top
(exemplo). Isso depende das regras da Apple que estão sendo migradas para plataformas.
Java
As regras Java do Bazel usam plataformas.
Isso substitui as flags legadas --java_toolchain
, --host_java_toolchain
,
--javabase
e --host_javabase
.
Para saber como usar as flags de configuração, consulte o manual Bazel e Java. Para mais informações, consulte o documento de design.
Se você ainda estiver usando flags legadas, siga o processo de migração em Problema nº 7849.
Android
As regras do Android do Bazel usam plataformas para selecionar conjuntos de ferramentas quando você define
--incompatible_enable_android_toolchain_resolution
.
Essa opção não é ativada por padrão. Mas a migração está bem encaminhada.
Apple
As regras do Bazel para Apple ainda não são compatíveis com plataformas para selecionar toolchains da Apple.
Elas também não oferecem suporte a dependências de C++ ativadas por plataforma porque usam o
legado --crosstool_top
para definir a cadeia de ferramentas de C++. Até que isso seja migrado, você
pode misturar projetos da Apple com C++ ativado para plataforma usando mapeamentos
de plataforma
(exemplo).
Outros idiomas
- As regras do Rust do Bazel oferecem suporte total a plataformas.
- As regras do Go do Bazel são totalmente compatíveis com plataformas (detalhes).
Se você estiver criando regras para uma nova linguagem, use plataformas para selecionar os conjuntos de ferramentas dela. Consulte a documentação de toolchains para um bom tutorial.
select()
Os projetos podem select()
em constraint_value
destinos, mas não em plataformas completas. Isso é intencional para que o select()
s seja compatível com a maior variedade possível de máquinas. Uma biblioteca com fontes específicas de ARM
precisa ser compatível com todas as máquinas com tecnologia ARM
, a menos que haja um motivo para ser mais específica.
Para selecionar em um ou mais constraint_value
s, use:
config_setting(
name = "is_arm",
constraint_values = [
"@platforms//cpu:arm",
],
)
Isso é equivalente a selecionar tradicionalmente em --cpu
:
config_setting(
name = "is_arm",
values = {
"cpu": "arm",
},
)
Confira mais detalhes aqui.
Os select
s em --cpu
, --crosstool_top
etc. não entendem --platforms
. Ao migrar seu projeto para plataformas, é necessário convertê-los para constraint_values
ou usar mapeamentos de plataforma para oferecer suporte aos dois estilos durante a janela de migração.
Transições
As transições do Starlark mudam
flags em partes do seu gráfico de build. Se o projeto usar uma transição que
defina --cpu
, --crossstool_top
ou outras flags legadas, as regras que leem
--platforms
não vão detectar essas mudanças.
Ao migrar seu projeto para plataformas, é necessário converter mudanças como
return { "//command_line_option:cpu": "arm" }
para return {
"//command_line_option:platforms": "//:my_arm_platform" }
ou usar mapeamentos de
plataforma para oferecer suporte aos dois estilos durante a janela
de migração.
Como usar plataformas hoje
Se você só quiser criar ou fazer a compilação cruzada de um projeto, siga a documentação oficial dele. Cabe aos mantenedores de linguagem e projeto determinar como e quando fazer a integração com plataformas e qual valor isso oferece.
Se você é responsável por um projeto, uma linguagem ou um conjunto de ferramentas e seu build não usa plataformas por padrão, você tem três opções (além de esperar a migração global):
Ative a flag "usar plataformas" para os idiomas do seu projeto (se houver uma) e faça os testes necessários para verificar se os projetos que você quer funcionam.
Se os projetos que você usa ainda dependem de flags legadas, como
--cpu
e--crosstool_top
, use-as com--platforms
:bazel build //:my_mixed_project --platforms==//:myplatform --cpu=... --crosstool_top=...
Isso tem um custo de manutenção (você precisa verificar manualmente se as configurações correspondem). Mas isso deve funcionar na ausência de transições renegadas.
Escreva mapeamentos de plataforma para oferecer suporte aos dois estilos mapeando configurações de estilo
--cpu
para as plataformas correspondentes e vice-versa.
Mapeamentos de plataforma
Os mapeamentos de plataforma são uma API temporária que permite que a lógica legada e baseada em plataforma coexistam na mesma build durante a janela de descontinuação da última.
Um mapeamento de plataforma é um mapa de um platform()
para um
conjunto correspondente de flags legadas ou o contrário. Exemplo:
platforms:
# Maps "--platforms=//platforms:ios" to "--cpu=ios_x86_64 --apple_platform_type=ios".
//platforms:ios
--cpu=ios_x86_64
--apple_platform_type=ios
flags:
# Maps "--cpu=ios_x86_64 --apple_platform_type=ios" to "--platforms=//platforms:ios".
--cpu=ios_x86_64
--apple_platform_type=ios
//platforms:ios
# Maps "--cpu=darwin --apple_platform_type=macos" to "//platform:macos".
--cpu=darwin
--apple_platform_type=macos
//platforms:macos
O Bazel usa isso para garantir que todas as configurações, baseadas em plataforma e legadas, sejam aplicadas de maneira consistente em todo o build, incluindo transições.
Por padrão, o Bazel lê mapeamentos do arquivo platform_mappings
na raiz do espaço de trabalho. Também é possível definir
--platform_mappings=//:my_custom_mapping
.
Confira aqui todos os detalhes.
Perguntas
Para suporte geral e dúvidas sobre o cronograma de migração, entre em contato com bazel-discuss@googlegroups.com ou os proprietários das regras apropriadas.
Para discussões sobre o design e a evolução das APIs da plataforma/cadeia de ferramentas, entre em contato com bazel-dev@googlegroups.com.