Migração para plataformas

Informar um problema Acessar fonte

O Bazel tem suporte sofisticado à modelagem de plataformas e conjuntos de ferramentas para várias arquiteturas e builds de compilação cruzada.

Esta página resume o estado desse suporte.

Consulte também:

Status

C++

As regras do C++ usam plataformas para selecionar conjuntos de ferramentas quando --incompatible_enable_cc_toolchain_resolution está definido.

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=...

Isso será ativado por padrão no Bazel 7.0 (#7260).

Para testar o projeto em C++ com plataformas, consulte Como migrar seu projeto e Como configurar conjuntos de ferramentas em C++.

Java

As regras Java usam plataformas para selecionar conjuntos de ferramentas.

Isso substitui as sinalizações legadas --java_toolchain, --host_java_toolchain, --javabase e --host_javabase.

Consulte Java e Bazel para mais detalhes.

Android

As regras do Android usam plataformas para selecionar conjuntos de ferramentas quando --incompatible_enable_android_toolchain_resolution está definido.

Isso significa que você pode configurar um projeto Android com:

bazel build //:my_android_project --android_platforms=//:my_android_platform

em vez de sinalizações legadas, como --android_crosstool_top, --android_cpu e --fat_apk_cpu.

Isso será ativado por padrão no Bazel 7.0 (#16285).

Para testar o projeto Android com plataformas, consulte Como migrar seu projeto.

Apple

As regras da Apple não são compatíveis com plataformas e ainda não estão programadas para suporte.

Você ainda pode usar APIs de plataforma com builds da Apple (por exemplo, ao criar com uma combinação de regras da Apple e C++ puro) com mapeamentos de plataforma.

Outros idiomas

  • As regras Go são totalmente compatíveis com plataformas
  • As regras do Rust oferecem suporte total a plataformas.

Se você tem um conjunto de regras de idioma, consulte Como migrar seu conjunto de regras para adicionar suporte.

Contexto

Plataformas e conjuntos de ferramentas foram introduzidos para padronizar a forma como os projetos de software são direcionados a diferentes arquiteturas e fazem compilação cruzada.

Isso foi inspirado pela observação de que os mantenedores de linguagem já estavam fazendo isso de maneiras específicas e incompatíveis. Por exemplo, as regras C++ usavam --cpu e --crosstool_top para declarar uma CPU e um conjunto de ferramentas de destino. Nenhuma dessas opções modela corretamente uma "plataforma". Isso produziu builds estranhos e incorretos.

Java, Android e outras linguagens desenvolveram as próprias flags para fins semelhantes, e nenhuma interoperava umas com as outras. Isso tornava as criações entre linguagens confusas e complicadas.

O Bazel é destinado a projetos grandes, em vários idiomas e em várias plataformas. Isso exige um suporte mais baseado em princípios para esses conceitos, incluindo uma API padrão clara.

Necessidade de migração

O upgrade para a nova API requer duas iniciativas: liberar a API e atualizar a lógica da regra para usá-la.

A primeira já foi concluída, mas a segunda está em andamento. Isso consiste em garantir que plataformas e conjuntos de ferramentas específicos da linguagem sejam definidos, a lógica da linguagem leia os conjuntos de ferramentas usando a nova API em vez de sinalizações antigas, como --crosstool_top, e config_settings selecionem a nova API em vez de sinalizações antigas.

Esse trabalho é simples, mas requer um esforço distinto para cada idioma, além de avisos justos para que os proprietários do projeto testem as próximas mudanças.

É por isso que essa é uma migração em andamento.

Meta

A migração estará concluída quando todos os projetos forem criados com o formulário:

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

Isso implica:

  1. As regras do projeto escolhem os conjuntos de ferramentas certos para //:myplatform.
  2. As dependências do projeto escolhem os conjuntos de ferramentas certos para //:myplatform.
  3. //:myplatform faz referência a declarações comuns de CPU, OS e outras propriedades genéricas independentes de linguagem.
  4. Todos os select()s relevantes correspondem corretamente a //:myplatform.
  5. //:myplatform é definido em um local claro e acessível: no repositório do projeto se a plataforma for exclusiva para ele, ou em algum lugar comum em que todos os projetos consumidores possam encontrá-lo.

Sinalizações antigas, como --cpu, --crosstool_top e --fat_apk_cpu, serão descontinuadas e removidas assim que for seguro.

Em última análise, essa será a única maneira de configurar arquiteturas.

Como migrar seu projeto

Se você criar com linguagens que oferecem suporte a plataformas, seu build já vai funcionar com uma invocação como:

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

Consulte o status e a documentação do seu idioma para mais detalhes.

Se um idioma exigir uma sinalização para ativar o suporte à plataforma, você também precisará defini-la. Consulte Status para mais detalhes.

Para que seu projeto seja criado, verifique o seguinte:

  1. //:myplatform precisa existir. Geralmente, é responsabilidade do proprietário do projeto definir plataformas, porque projetos diferentes visam máquinas distintas. Consulte Plataformas padrão.

  2. Os conjuntos de ferramentas que você quer usar precisam existir. Se você estiver usando conjuntos de ferramentas de bancos de imagens, os proprietários de linguagem precisarão incluir instruções sobre como registrá-los. Se você estiver criando seus próprios conjuntos de ferramentas personalizados, será necessário register no arquivo MODULE.bazel ou com --extra_toolchains.

  3. select()s e transições de configuração precisam ser resolvidas corretamente. Consulte select() e Transições.

  4. Se o build mistura linguagens que oferecem suporte ou não a plataformas, talvez os mapeamentos de plataforma sejam necessários para ajudar os idiomas legados a funcionarem com a nova API. Consulte Mapeamentos de plataforma para mais detalhes.

Se você ainda tiver problemas, entre em contato para receber suporte.

Plataformas padrão

Os proprietários de projetos precisam definir plataformas explícitas que descrevam as arquiteturas para as quais querem criar. Em seguida, eles são acionados com --platforms.

Quando --platforms não está definido, o Bazel assume como padrão uma platform que representa a máquina de build local. Ele é gerado automaticamente em @platforms//host (aliasado como @bazel_tools//tools:host_platform) por isso não é necessário defini-lo explicitamente. Ele mapeia o OS e o CPU da máquina local com constraint_values declarados em @platforms.

select()

Os projetos podem select() em destinos constraint_value, mas não em plataformas completas. Isso é intencional para que select() ofereça suporte à maior variedade 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 motivo para ser mais específica.

Para selecionar em um ou mais constraint_values, use:

config_setting(
    name = "is_arm",
    constraint_values = [
        "@platforms//cpu:arm",
    ],
)

Isso é equivalente à seleção tradicional no --cpu:

config_setting(
    name = "is_arm",
    values = {
        "cpu": "arm",
    },
)

Confira mais detalhes neste link.

selects em --cpu, --crosstool_top etc. não entendem --platforms. Ao migrar seu projeto para plataformas, você precisa convertê-las em constraint_values ou usar mapeamentos de plataforma para oferecer suporte a ambos os estilos durante a migração.

Transições

As transições do Starlark modificam as sinalizações para partes do gráfico de build. Se o projeto usar uma transição que defina --cpu, --crossstool_top ou outras sinalizações legadas, as regras que leem --platforms não terão 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 durante a janela.

Como migrar seu grupo de regras

Se você possui um grupo de regras e deseja oferecer suporte a plataformas, você precisa:

  1. Faça com que a lógica de regras resolva os conjuntos de ferramentas com a API de conjunto de ferramentas. Consulte a API do conjunto de ferramentas (ctx.toolchains).

  2. Opcional: defina uma sinalização --incompatible_enable_platforms_for_my_language para que a lógica da regra resolva os conjuntos de ferramentas por meio da nova API ou de sinalizações antigas, como --crosstool_top, durante o teste de migração.

  3. Defina as propriedades relevantes que compõem os componentes da plataforma. Consulte Propriedades comuns da plataforma.

  4. Defina conjuntos de ferramentas padrão e torne-os acessíveis aos usuários por meio das instruções de registro da regra (detalhes).

  5. Verifique se select()s e transições de configuração oferecem suporte a plataformas. Esse é o maior desafio. Isso é particularmente desafiador para projetos com vários idiomas, o que pode falhar se todos os idiomas não puderem ler --platforms.

Se você precisar misturar regras que não oferecem suporte a plataformas, pode ser necessário mapeamentos de plataforma para preencher a lacuna.

Propriedades comuns da plataforma

Propriedades comuns da plataforma em vários idiomas, como OS e CPU, precisam ser declaradas em @platforms. Isso incentiva o compartilhamento, a padronização e a compatibilidade entre idiomas.

As propriedades exclusivas das suas regras precisam ser declaradas no repositório delas. Isso permite que você mantenha uma propriedade clara sobre os conceitos específicos pelos quais as regras são responsáveis.

Se as regras usam SOs ou CPUs de finalidade personalizada, elas precisam ser declaradas no repositório da regra em vez de @platforms.

Mapeamentos de plataforma

Os mapeamentos de plataforma são uma API temporária que permite que a lógica com reconhecimento de plataforma se misture com a lógica legada no mesmo build. Essa é uma ferramenta contundente destinada apenas a suavizar as incompatibilidades com diferentes períodos de migração.

Um mapeamento de plataforma é um mapa de uma platform() para um conjunto correspondente de sinalizações legadas ou o inverso. Exemplo:

platforms:
  # Maps "--platforms=//platforms:ios" to "--ios_multi_cpus=x86_64 --apple_platform_type=ios".
  //platforms:ios
    --ios_multi_cpus=x86_64
    --apple_platform_type=ios

flags:
  # Maps "--ios_multi_cpus=x86_64 --apple_platform_type=ios" to "--platforms=//platforms:ios".
  --ios_multi_cpus=x86_64
  --apple_platform_type=ios
    //platforms:ios

  # Maps "--cpu=darwin_x86_64 --apple_platform_type=macos" to "//platform:macos".
  --cpu=darwin_x86_64
  --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 todo o build, inclusive por transições.

Por padrão, o Bazel lê mapeamentos do arquivo platform_mappings na raiz do espaço de trabalho. Você também pode definir --platform_mappings=//:my_custom_mapping.

Consulte o design de mapeamentos de plataforma para mais detalhes.

Revisão da 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 constraint_setting comum:

constraint_setting(name = "os")
constraint_value(
    name = "linux",
    constraint_setting = ":os",
)
constraint_value(
    name = "mac",
    constraint_setting = ":os",
)

Uma toolchain é uma regra Starlark. Os atributos dela 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.

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 compilar $ bazel build //:myproject --platforms=//:myplatform, o Bazel seleciona automaticamente um conjunto de ferramentas que pode ser executado na máquina de build e em binários de build para //:myplatform. Isso é conhecido como resolução do conjunto de ferramentas.

O conjunto de conjuntos de ferramentas disponíveis pode ser registrado no arquivo MODULE.bazel com register_toolchains ou na linha de comando com --extra_toolchains.

Confira mais informações aqui.

Dúvidas

Para suporte geral e perguntas sobre o cronograma de migração, entre em contato com bazel-discuss ou 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 (link em inglês).

Veja também