O Bazel oferece suporte sofisticado para plataformas de modelagem platforms e toolchains. A integração com projetos reais exige uma 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.
tl;dr:as APIs de plataforma e conjunto de ferramentas do Bazel estão disponíveis, mas não funcionarão em todos os lugares até que todas as regras de linguagem, select()s e outras referências legadas sejam atualizadas. Esse trabalho é constante. Eventualmente, todas as builds serão baseadas em plataforma.
Leia abaixo para saber onde suas builds se encaixam.
Para mais documentação formal, consulte:
Contexto
As plataformas e os conjuntos de ferramentas foram introduzidos para padronizar como os projetos de software segmentam máquinas diferentes e criam com as ferramentas de linguagem certas.
Essa é uma adição relativamente recente ao Bazel. Ela foi
inspirada
na observação de que os mantenedores de linguagem estavam já fazendo isso de maneiras ad
hoc e incompatíveis. Por exemplo, as regras de C++ usam --cpu e --crosstool_top para definir a CPU de destino e o conjunto de ferramentas de C++ de uma build. Nenhum deles modela corretamente uma "plataforma". As tentativas históricas de fazer isso causaram builds estranhas e imprecisas.
Essas flags também não controlam a compilação Java, que desenvolveu sua própria interface independente com --java_toolchain.
O Bazel é destinado a projetos grandes, multilíngues e multiplataformas. Isso exige um suporte mais fundamentado para esses conceitos, incluindo APIs claras que incentivam a interoperabilidade de linguagem e projeto. É para isso que essas novas APIs servem.
Migração
As APIs de plataforma e conjunto de ferramentas só funcionam quando os projetos as usam. Isso não é trivial porque a lógica de regra, os conjuntos de ferramentas, as dependências e os select()s de um projeto precisam oferecer suporte a elas. Isso exige uma sequência de migração cuidadosa para manter todos os projetos e dependências funcionando corretamente.
Por exemplo, as regras de C++ do Bazel oferecem suporte a plataformas. Mas as regras da Apple não. Seu projeto em C++ pode não se importar com a Apple. Mas outros podem. Portanto, ainda não é seguro ativar globalmente as plataformas para todas as builds em C++.
O restante desta página descreve essa sequência de migração e como e quando seus projetos podem se encaixar.
Objetivo
A migração da plataforma do Bazel é concluída quando todos os projetos são criados com o formulário:
bazel build //:myproject --platforms=//:myplatformIsso 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 que dependem do seu oferecem suporte a
//:myplatformou o projeto oferece suporte às APIs legadas (como--crosstool_top). //:myplatformfaz referência a [declarações comuns][Common Platform Declaration]{: .external} deCPU,OSe 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, ou em algum lugar que todos os projetos que possam usar essa plataforma possam encontrar.
As APIs antigas serão removidas assim que essa meta for alcançada. Essa será a maneira padrão como os projetos selecionam plataformas e conjuntos de ferramentas.
Devo usar plataformas?
Se você quiser apenas criar ou compilar um projeto, siga a documentação oficial do projeto.
Se você for um mantenedor de projetos, idiomas ou conjuntos de ferramentas, vai querer oferecer suporte às novas APIs. Se você espera até que a migração global seja concluída ou opte por participar mais cedo, depende das suas necessidades específicas de valor / custo:
Valor
- É possível
select()ou escolher conjuntos de ferramentas nas propriedades exatas que você quer, em vez de flags codificadas, como--cpu. Por exemplo, várias CPUs podem oferecer suporte ao mesmo conjunto de instruções. - Builds mais corretas. Se você
select()com--cpuno exemplo acima e adicionar uma nova CPU que ofereça suporte ao mesmo conjunto de instruções, oselect()não vai reconhecer a nova CPU. Mas umselect()em plataformas permanece 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 linguagem na linha de comando. - Design de linguagem mais simples. Todas as linguagens compartilham uma API comum para definir conjuntos de ferramentas, usar conjuntos de ferramentas e selecionar o conjunto de ferramentas certo para uma plataforma.
- Os destinos podem ser ignorados na fase de build e teste se forem incompatíveis com a plataforma segmentada.
Custos
- Projetos dependentes que ainda não oferecem suporte a plataformas podem não funcionar automaticamente com o seu.
- Para que eles funcionem, pode ser necessária uma manutenção temporária adicional.
- A coexistência de APIs novas e legadas exige uma orientação mais cuidadosa do usuário para evitar confusão.
- As definições canônicas para propriedades comuns como
OSeCPUainda estão em evolução e podem exigir contribuições iniciais extras. - As definições canônicas para conjuntos de ferramentas específicos de linguagem ainda estão em evolução e podem exigir contribuições iniciais extras.
Revisão pela API
Um platform é uma coleção de destinos
constraint_value:
platform(
name = "myplatform",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:arm",
],
)
Um constraint_value é uma propriedade
da máquina. Os valores do mesmo "tipo" são agrupados em um comum
constraint_setting:
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 um idioma (como compiler =
"//mytoolchain:custom_gcc"). Os provedores transmitem
essas informações para regras que precisam ser criadas com essas ferramentas.
Os conjuntos de ferramentas declaram os constraint_values 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 um conjunto de ferramentas que pode ser executado na máquina de build e
criar binários para //:myplatform. Isso é conhecido como resolução do conjunto de ferramentas.
O conjunto de conjuntos de ferramentas disponíveis pode ser registrado no WORKSPACE com
register_toolchains ou na
linha de comando com --extra_toolchains.
Consulte este link para mais detalhes.
Status
O suporte atual da plataforma varia entre os idiomas. Todas as principais regras do Bazel estão sendo migradas para plataformas. Mas esse processo vai levar tempo. Isso ocorre por três motivos principais:
A lógica de regra precisa ser atualizada para receber informações da ferramenta da nova API do conjunto de ferramentas (
ctx.toolchains) e parar de ler configurações legadas, como--cpue--crosstool_top. Isso é relativamente simples.Os mantenedores de conjuntos de ferramentas precisam definir conjuntos de ferramentas e torná-los acessíveis aos 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). Geralmente, os projetos precisam definir as próprias plataformas.
Os projetos atuais precisam ser migrados.
select()s e transições também precisam ser migradas. Esse é o maior desafio. É particularmente desafiador para projetos multilíngues (que podem falhar se todas as linguagens não puderem ler--platforms).
Se você estiver criando um novo conjunto de regras, ofereça suporte a plataformas desde o início. Isso torna suas regras automaticamente compatíveis com outras regras e projetos, com valor crescente à medida que a API da plataforma se torna mais onipresente.
Propriedades comuns da plataforma
As 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 idiomas.
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
@platforms repositório
(portanto, o rótulo canônico para o exemplo acima é @platforms//cpu:arm).
As propriedades comuns de linguagem precisam ser declaradas nos repositórios das respectivas
linguagens.
Plataformas padrão
Geralmente, os proprietários do projeto precisam definir plataformas explícitas
para descrever os
tipos de máquinas para as quais querem criar. Elas são acionadas com --platforms.
Quando --platforms não está definido, o Bazel usa como padrão uma platform que representa a
máquina de build local. Ela é gerada automaticamente em @local_config_platform//:host, então não é necessário defini-la explicitamente. Ela mapeia o OS
e CPU da máquina local com constraint_values declarados em
@platforms.
C++
As regras de C++ do Bazel usam plataformas para selecionar conjuntos de ferramentas quando você define
--incompatible_enable_cc_toolchain_resolution
(#7260).
Isso significa que você pode configurar um projeto em C++ com:
bazel build //:my_cpp_project --platforms=//:myplatformem 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 em C++, você poderá usar
plataformas com segurança, desde que seus selects e
transições sejam compatíveis. Consulte
#7260 e
Como configurar conjuntos de ferramentas de C++ para mais orientações.
Esse modo não está ativado por padrão. Isso ocorre porque os projetos da Apple
ainda configuram dependências de C++ com --cpu e --crosstool_top
(exemplo). Portanto, isso depende da migração das regras da Apple 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 do Bazel e do Java. Para mais informações, consulte o documento de design.
Se você ainda estiver usando flags legadas, siga o processo de migração no 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 está ativada por padrão. Mas a migração está bem encaminhada.
Apple
As regras da Apple do Bazel ainda não oferecem suporte a plataformas para selecionar conjuntos de ferramentas da Apple.
Elas também não oferecem suporte a dependências de C++ ativadas por plataforma porque usam o --crosstool_top legado para definir o conjunto de ferramentas de C++. Até que isso seja migrado, você
pode misturar projetos da Apple com C++ ativado por plataforma com mapeamentos
de plataforma
(exemplo).
Outros idiomas
- As regras do Rust do Bazel oferecem suporte total a plataformas.
- As regras do Go do Bazel oferecem suporte total a plataformas (detalhes).
Se você estiver criando regras para um novo idioma, use plataformas para selecionar os conjuntos de ferramentas do idioma. Consulte a documentação de conjuntos de ferramentas para um bom tutorial.
select()
Os projetos podem select() em
constraint_value destinos mas não em plataformas completas. Isso é intencional para que select()s ofereça suporte ao maior número possível de máquinas. Uma biblioteca com fontes específicas de ARM precisa oferecer suporte a todas as máquinas com tecnologia ARM, a menos que haja um motivo para ser mais específico.
Para selecionar um ou mais constraint_values, use:
config_setting(
name = "is_arm",
constraint_values = [
"@platforms//cpu:arm",
],
)
Isso é equivalente à seleção tradicional em --cpu:
config_setting(
name = "is_arm",
values = {
"cpu": "arm",
},
)
Mais detalhes aqui.
selects em --cpu, --crosstool_top etc. não entendem --platforms. Ao
migrar seu projeto para plataformas, você precisa convertê-los em
constraint_values ou usar mapeamentos de plataforma para oferecer suporte
aos dois estilos na janela de migração.
Transições
As transições do Starlark mudam
as flags em partes do gráfico de build. Se o projeto usar uma transição que define --cpu, --crossstool_top ou outras flags legadas, as regras que leem --platforms não vão ver essas mudanças.
Ao migrar seu projeto para plataformas, você precisa converter mudanças como
return { "//command_line_option:cpu": "arm" } em return {
"//command_line_option:platforms": "//:my_arm_platform" } ou usar mapeamentos de plataforma para oferecer suporte aos dois estilos na janela de migração.
Como usar plataformas hoje
Se você quiser apenas criar ou compilar um projeto, siga a documentação oficial do projeto. Cabe aos mantenedores de linguagem e projeto determinar como e quando integrar com plataformas e qual valor isso oferece.
Se você for um mantenedor de projetos, idiomas ou conjuntos de ferramentas e sua build não usar plataformas por padrão, terá três opções (além de aguardar a migração global):
Ative a flag "usar plataformas" para os idiomas do projeto (se houver uma) e faça os testes necessários para verificar se os projetos de que você se importa funcionam.
Se os projetos de que você se importa ainda dependem de flags legadas, como
--cpue--crosstool_top, use-as com--platforms:bazel build //:my_mixed_project --platforms==//:myplatform --cpu=... --crosstool_top=...Isso tem algum custo de manutenção (é necessário garantir manualmente que as configurações correspondam). Mas isso precisa funcionar na ausência de transições desonestas .
Escreva mapeamentos de plataforma para oferecer suporte aos dois estilos mapeando as configurações de estilo
--cpupara as plataformas correspondentes e vice-versa.
Mapeamentos de plataforma
Mapeamentos de plataforma é uma API temporária que permite que a lógica com tecnologia de plataforma e legada coexistam na mesma build durante a janela de suspensão da última.
Um mapeamento de plataforma é um mapa de um platform() para um conjunto correspondente de flags legadas ou o inverso. 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 forma consistente em toda a build, incluindo transições.
Por padrão, o Bazel lê mapeamentos do arquivo platform_mappings na raiz do seu espaço de trabalho. Também é possível definir --platform_mappings=//:my_custom_mapping.
Consulte este link para detalhes completos.
Perguntas
Para suporte geral e perguntas sobre o cronograma de migração, entre em contato com bazel-discuss@googlegroups.com ou com os proprietários das regras apropriadas.
Para discussões sobre o design e a evolução das APIs de plataforma/conjunto de ferramentas, entre em contato com bazel-dev@googlegroups.com.