Como criar com plataformas

Informar um problema Ver a fonte Nightly · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

O Bazel tem suporte sofisticado para modelagem de plataformas 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.

Resumo:as APIs da plataforma e da 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 legados sejam atualizados. Esse trabalho é constante. Eventualmente, todos os builds serão baseados em plataformas. Leia abaixo para saber onde seus builds se encaixam.

Para consultar uma documentação mais formal, consulte:

Contexto

Plataformas e toolchains foram introduzidos para padronizar como os projetos de software visam diferentes máquinas e são criados 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 linguagem estavam fazendo isso de maneiras ad hoc e incompatíveis. Por exemplo, as regras C++ usam --cpu e --crosstool_top para definir a CPU de destino e o conjunto de ferramentas C++ de um build. Nenhum deles modela corretamente uma "plataforma". As tentativas anteriores de fazer isso causaram builds estranhos e imprecisos. Essas flags também não controlam a compilação do Java, que evoluiu a própria interface independente com --java_toolchain.

O Bazel é destinado a projetos grandes, multilíngues e multiplataforma. Isso exige mais suporte a esses conceitos, incluindo APIs claras que incentivam a interoperabilidade de linguagem e projeto. É para isso que essas novas APIs servem.

Migração

As APIs da plataforma e da cadeia de ferramentas só funcionam quando os projetos as usam. Isso não é trivial porque a lógica de regras, as cadeias de ferramentas, as dependências e os select()s de um projeto precisam oferecer suporte a eles. Isso requer uma sequência de migração cuidadosa para manter todos os projetos e as dependências funcionando corretamente.

Por exemplo, as regras 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. No entanto, 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 é concluída quando todos os projetos são criados com o formulário:

bazel build //:myproject --platforms=//:myplatform

Isso implica:

  1. As regras usadas pelo seu projeto podem inferir conjuntos de ferramentas corretos de //:myplatform.
  2. As regras usadas pelas dependências do projeto podem inferir os conjuntos de ferramentas corretos de //:myplatform.
  3. Os projetos dependem do suporte do //:myplatform ou seu projeto oferece suporte às APIs legadas (como --crosstool_top).
  4. //:myplatform faz referência a [declarações comuns][Common Platform Declaration]{: .external} de CPU, OS e outros conceitos genéricos que oferecem suporte à compatibilidade automática entre projetos.
  5. Todos os select()s dos projetos relevantes entendem as propriedades da máquina implícitas em //:myplatform.
  6. //:myplatform é definido em um local claro e reutilizável: no repositório do projeto, se a plataforma for exclusiva para ele. 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 esse objetivo for alcançado. Essa será a maneira padrão de seleção de plataformas e cadeias de ferramentas.

Devo usar plataformas?

Se você quiser apenas criar ou fazer uma compilação cruzada de um projeto, siga a documentação oficial do projeto.

Se você é um mantenedor de projeto, linguagem ou cadeia de ferramentas, vai querer oferecer suporte às novas APIs. A decisão de esperar até que a migração global seja concluída ou ativar o recurso mais cedo depende das suas necessidades específicas de valor / custo:

Valor

  • É possível usar select() ou escolher toolchains nas propriedades exatas que você considera importantes em vez de flags codificadas, como --cpu. Por exemplo, várias CPUs podem oferecer suporte ao mesmo conjunto de instruções.
  • Builds mais corretos. Se você select() com --cpu no exemplo acima e adicionar uma nova CPU compatível com o mesmo conjunto de instruções, o select() não reconhecerá a nova CPU. Mas um select() nas 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. Todos os idiomas compartilham uma API comum para definir e usar toolchains e selecionar a toolchain 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 são compatíveis com plataformas podem não funcionar automaticamente com o seu.
  • Para que eles funcionem, talvez seja necessário fazer manutenção temporária adicional.
  • A coexistência de APIs novas e legados exige uma orientação mais cuidadosa ao usuário para evitar confusões.
  • As definições canônicas para propriedades comuns, como OS e CPU, ainda estão em evolução e podem exigir contribuições iniciais extras.
  • As definições canônicas para toolchains 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 alvo constraint_value:

platform(
    name = "myplatform",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:arm",
    ],
)

Um constraint_value é uma propriedade de máquina. Os 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 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 cadeias de ferramentas declaram as constraint_values de máquinas que podem ser direcionadas (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 de 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.

Confira este link para saber mais.

Status

O suporte atual da plataforma varia de acordo com o idioma. Todas as regras principais do Bazel estão sendo migradas para plataformas. Mas esse processo pode demorar. Isso acontece por três motivos principais:

  1. 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 --cpu e --crosstool_top. Isso é relativamente simples.

  2. Os mantenedores de toolchains precisam definir toolchains 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 fácil para o usuário.

    As definições de plataforma também são necessárias, a menos que você esteja criando para a mesma máquina em que o Bazel é executado. Geralmente, os projetos precisam definir as próprias plataformas.

  3. Os projetos atuais precisam ser migrados. select()s e transições também precisam ser migrados. Esse é o maior desafio. Isso é particularmente desafiador para projetos multilíngues, que podem falhar se todos os idiomas não conseguirem ler --platforms.

Se você estiver projetando um novo conjunto de regras, será necessário oferecer suporte a plataformas desde o início. Isso torna suas regras compatíveis automaticamente com outras regras e projetos, com valor crescente à medida que a API da plataforma se torna mais onipresente.

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 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 em todo o mundo são declaradas no repositório @platforms (o rótulo canônico do exemplo acima é @platforms//cpu:arm). As propriedades comuns a todos os idiomas precisam ser declaradas nos repositórios dos respectivos idiomas.

Plataformas padrão

Geralmente, os proprietários de projetos precisam definir plataformas explícitas para descrever os tipos de máquinas para os quais eles querem criar. Elas são acionadas com --platforms.

Quando --platforms não está definido, o Bazel usa o padrão platform, que representa a máquina de build local. Isso é 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_values declarados em @platforms.

C++

As regras C++ do Bazel usam plataformas para selecionar toolchains quando você define --incompatible_enable_cc_toolchain_resolution (#7260).

Isso significa que você pode configurar um projeto 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 são C++, você poderá usar plataformas com segurança, desde que as selects e transições sejam compatíveis. Consulte #7260 e Como configurar toolchains do C++ para mais orientações.

Esse modo não é ativado por padrão. Isso ocorre porque os projetos da Apple ainda configuram dependências de C++ com --cpu e --crosstool_top (exemplo). Isso depende das regras da Apple migrando para as plataformas.

Java

As regras Java do Bazel usam plataformas.

Isso substitui as flags --java_toolchain, --host_java_toolchain, --javabase e --host_javabase legadas.

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 Issue #7849.

Android

As regras do Bazel para Android usam plataformas para selecionar toolchains quando você define --incompatible_enable_android_toolchain_resolution.

Essa opção não é ativada por padrão. Mas a migração está a caminho.

Apple

As regras da Apple do Bazel ainda não são compatíveis com plataformas para selecionar cadeias de ferramentas da Apple.

Elas também não oferecem suporte a dependências C++ ativadas pela plataforma porque usam o --crosstool_top legado para definir a cadeia de ferramentas C++. Até que isso seja migrado, você pode misturar projetos da Apple com C++ habilitado para plataforma com mapeamentos de plataforma (exemplo).

Outros idiomas

Se você estiver criando regras para um novo idioma, use plataformas para selecionar os conjuntos de ferramentas do idioma. Consulte a documentação de toolchains para conferir um bom tutorial.

select()

Os projetos podem select() em constraint_value de destino, mas não em plataformas completas. Isso é intencional para que select()s ofereça suporte a uma variedade de máquinas o mais ampla possível. Uma biblioteca com fontes específicas de ARM precisa oferecer suporte a todas as máquinas ARM, a menos que haja motivos 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, é necessário convertê-lo em constraint_values ou usar mapeamentos de plataforma para oferecer suporte a ambos os estilos durante a janela de migração.

Transições

As transições do Starlark mudam as flags para 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 notar 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 as plataformas hoje

Se você quiser apenas criar ou fazer a compilação cruzada de um projeto, siga a documentação oficial do projeto. Cabe aos mantenedores de linguagem e de projeto determinar como e quando integrar com as plataformas e qual valor isso oferece.

Se você é um mantenedor de projeto, linguagem ou 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):

  1. Ative a flag "use platforms" para as linguagens do seu projeto (se houver uma) e faça os testes necessários para saber se os projetos que você quer trabalhar funcionam.

  2. Se os projetos 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 alguns custos de manutenção (é necessário verificar manualmente se as configurações correspondem). No entanto, isso deve funcionar na ausência de transições irregulares.

  3. Crie mapeamentos de plataforma para oferecer suporte aos dois estilos, mapeando as configurações do 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 com suporte à plataforma e a legada coexistam no mesmo build durante a janela de descontinuação do legado.

Um mapeamento de plataforma é um mapeamento de um platform() para um conjunto correspondente de flags legados 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 na plataforma e legado, sejam aplicadas de forma 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 com os proprietários das regras apropriadas.

Para discussões sobre o design e a evolução das APIs da plataforma/ferramenta, entre em contato com bazel-dev@googlegroups.com.

Consulte também