Este tutorial usa um cenário de exemplo para descrever como configurar os conjuntos de ferramentas C++ para um projeto.
O que você vai aprender
Neste tutorial, você vai aprender a:
- Configurar o ambiente de build
- Usar
--toolchain_resolution_debug
para depurar a resolução do conjunto de ferramentas. - Configurar o conjunto de ferramentas C++
- Crie uma regra do Starlark que forneça configuração
extra para o
cc_toolchain
para que o Bazel possa criar o aplicativo comclang
. - Crie o binário C++ executando
bazel build //main:hello-world
em uma máquina Linux - Faça a compilação cruzada do binário para Android executando
bazel build //main:hello-world --platforms=//:android_x86_64
.
Antes de começar
Este tutorial pressupõe que você está no Linux e já criou aplicativos C++ e instalou as ferramentas e bibliotecas apropriadas.
Neste tutorial, usamos clang version 16
, que pode ser instalado no sistema.
Configurar o ambiente de build
Configure seu ambiente de build da seguinte maneira:
Faça o download e instale o Bazel 7.0.2 ou posterior, caso ainda não tenha feito isso.
Adicione um arquivo
WORKSPACE
vazio à pasta raiz.Adicione o seguinte destino
cc_binary
ao arquivomain/BUILD
:load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
Como o Bazel usa muitas ferramentas internas escritas em C++ durante a compilação, como
process-wrapper
, o conjunto de ferramentas C++ padrão preexistente é especificado para a plataforma do host. Isso permite que essas ferramentas internas sejam criadas usando o conjunto de ferramentas criado neste tutorial. Portanto, o destinocc_binary
também é criado com o conjunto de ferramentas padrão.Execute o build com o seguinte comando:
bazel build //main:hello-world
A compilação é bem-sucedida sem nenhum conjunto de ferramentas registrado em
WORKSPACE
.Para acessar mais detalhes sobre o que está sob a cobertura, execute:
bazel build //main:hello-world --toolchain_resolution_debug='@bazel_tools//tools/cpp:toolchain_type' INFO: ToolchainResolution: Target platform @@local_config_platform//:host: Selected execution platform @@local_config_platform//:host, type @@bazel_tools//tools/cpp:toolchain_type -> toolchain @@bazel_tools~cc_configure_extension~local_config_cc//:cc-compiler-k8
Sem especificar
--platforms
, o Bazel cria o destino para@local_config_platform//:host
usando@bazel_tools//cc_configure_extension/local_config_cc//:cc-compiler-k8
.
Configurar o conjunto de ferramentas C++
Para configurar o conjunto de ferramentas C++, crie o aplicativo repetidamente e elimine cada erro um por um, conforme descrito a seguir.
Ele também pressupõe clang version 9.0.1
, embora os detalhes só mudem
um pouco entre as diferentes versões do Clang.
Adicionar
toolchain/BUILD
comfilegroup(name = "empty") cc_toolchain( name = "linux_x86_64_toolchain", toolchain_identifier = "linux_x86_64-toolchain", toolchain_config = ":linux_x86_64_toolchain_config", all_files = ":empty", compiler_files = ":empty", dwp_files = ":empty", linker_files = ":empty", objcopy_files = ":empty", strip_files = ":empty", supports_param_files = 0, ) toolchain( name = "cc_toolchain_for_linux_x86_64", toolchain = ":linux_x86_64_toolchain", toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", exec_compatible_with = [ "@platforms//cpu:x86_64", "@platforms//os:linux", ], target_compatible_with = [ "@platforms//cpu:x86_64", "@platforms//os:linux", ], )
Em seguida, registre o conjunto de ferramentas com o
WORKSPACE
com oregister_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64" )
Esta etapa define um
cc_toolchain
e o vincula a um destinotoolchain
para a configuração do host.Execute o build novamente. Como o pacote
toolchain
ainda não define o destinolinux_x86_64_toolchain_config
, o Bazel gera o seguinte erro:ERROR: toolchain/BUILD:4:13: in toolchain_config attribute of cc_toolchain rule //toolchain:linux_x86_64_toolchain: rule '//toolchain:linux_x86_64_toolchain_config' does not exist.
No arquivo
toolchain/BUILD
, defina um grupo de arquivos vazio da seguinte maneira:package(default_visibility = ["//visibility:public"]) filegroup(name = "linux_x86_64_toolchain_config")
Execute o build novamente. O Bazel gera o seguinte erro:
'//toolchain:linux_x86_64_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'.
CcToolchainConfigInfo
é um provedor usado para configurar os conjuntos de ferramentas em C++. Para corrigir esse erro, crie uma regra do Starlark que forneçaCcToolchainConfigInfo
ao Bazel criando um arquivotoolchain/cc_toolchain_config.bzl
com o seguinte conteúdo:def _impl(ctx): return cc_common.create_cc_toolchain_config_info( ctx = ctx, toolchain_identifier = "k8-toolchain", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", ) cc_toolchain_config = rule( implementation = _impl, attrs = {}, provides = [CcToolchainConfigInfo], )
cc_common.create_cc_toolchain_config_info()
cria o provedor necessárioCcToolchainConfigInfo
. Para usar a regracc_toolchain_config
, adicione uma instrução de carregamento atoolchain/BUILD
logo abaixo da instrução do pacote:load(":cc_toolchain_config.bzl", "cc_toolchain_config")
E substitua o grupo de arquivos "linux_x86_64_dataset_config" por uma declaração de uma regra
cc_toolchain_config
:cc_toolchain_config(name = "linux_x86_64_toolchain_config")
Execute o build novamente. O Bazel gera o seguinte erro:
.../BUILD:1:1: C++ compilation of rule '//:hello-world' failed (Exit 1) src/main/tools/linux-sandbox-pid1.cc:421: "execvp(toolchain/DUMMY_GCC_TOOL, 0x11f20e0)": No such file or directory Target //:hello-world failed to build`
Neste ponto, o Bazel tem informações suficientes para tentar criar o código, mas ainda não sabe quais ferramentas usar para concluir as ações de build necessárias. Você modificará a implementação da regra do Starlark para informar ao Bazel quais ferramentas usar. Para isso, você precisa do construtor tool_path() de
@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl
:# toolchain/cc_toolchain_config.bzl: # NEW load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "tool_path") def _impl(ctx): tool_paths = [ # NEW tool_path( name = "gcc", path = "/usr/bin/clang", ), tool_path( name = "ld", path = "/usr/bin/ld", ), tool_path( name = "ar", path = "/usr/bin/ar", ), tool_path( name = "cpp", path = "/bin/false", ), tool_path( name = "gcov", path = "/bin/false", ), tool_path( name = "nm", path = "/bin/false", ), tool_path( name = "objdump", path = "/bin/false", ), tool_path( name = "strip", path = "/bin/false", ), ] return cc_common.create_cc_toolchain_config_info( ctx = ctx, toolchain_identifier = "local", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", tool_paths = tool_paths, # NEW )
Confira se
/usr/bin/clang
e/usr/bin/ld
são os caminhos corretos para o sistema.Execute o build novamente. O Bazel gera o seguinte erro:
ERROR: main/BUILD:3:10: Compiling main/hello-world.cc failed: absolute path inclusion(s) found in rule '//main:hello-world': the source file 'main/hello-world.cc' includes the following non-builtin files with absolute paths (if these are builtin files, make sure these paths are in your toolchain): '/usr/include/c++/13/ctime' '/usr/include/x86_64-linux-gnu/c++/13/bits/c++config.h' '/usr/include/x86_64-linux-gnu/c++/13/bits/os_defines.h' ...
O Bazel precisa saber onde pesquisar cabeçalhos incluídos. Há várias maneiras de resolver isso, como usar o atributo
includes
decc_binary
, mas isso é resolvido no nível do conjunto de ferramentas com o parâmetrocxx_builtin_include_directories
decc_common.create_cc_toolchain_config_info
. Esteja ciente de que, se você estiver usando uma versão diferente declang
, o caminho de inclusão será diferente. Esses caminhos também podem ser diferentes, dependendo da distribuição.Modifique o valor de retorno em
toolchain/cc_toolchain_config.bzl
para ficar assim:return cc_common.create_cc_toolchain_config_info( ctx = ctx, cxx_builtin_include_directories = [ # NEW "/usr/lib/llvm-16/lib/clang/16/include", "/usr/include", ], toolchain_identifier = "local", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", tool_paths = tool_paths, )
Execute o comando de build novamente. Um erro como este será exibido:
/usr/bin/ld: bazel-out/k8-fastbuild/bin/main/_objs/hello-world/hello-world.o: in function `print_localtime()': hello-world.cc:(.text+0x68): undefined reference to `std::cout'
O motivo para isso é que o vinculador não tem a biblioteca C++ padrão e não consegue encontrar os símbolos. Há muitas maneiras de resolver isso, como usar o atributo
linkopts
decc_binary
. Ele é resolvido ao garantir que qualquer destino que use o conjunto de ferramentas não precise especificar essa flag.Copie o seguinte código para
toolchain/cc_toolchain_config.bzl
:# NEW load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") # NEW load( "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "feature", # NEW "flag_group", # NEW "flag_set", # NEW "tool_path", ) all_link_actions = [ # NEW ACTION_NAMES.cpp_link_executable, ACTION_NAMES.cpp_link_dynamic_library, ACTION_NAMES.cpp_link_nodeps_dynamic_library, ] def _impl(ctx): tool_paths = [ tool_path( name = "gcc", path = "/usr/bin/clang", ), tool_path( name = "ld", path = "/usr/bin/ld", ), tool_path( name = "ar", path = "/bin/false", ), tool_path( name = "cpp", path = "/bin/false", ), tool_path( name = "gcov", path = "/bin/false", ), tool_path( name = "nm", path = "/bin/false", ), tool_path( name = "objdump", path = "/bin/false", ), tool_path( name = "strip", path = "/bin/false", ), ] features = [ # NEW feature( name = "default_linker_flags", enabled = True, flag_sets = [ flag_set( actions = all_link_actions, flag_groups = ([ flag_group( flags = [ "-lstdc++", ], ), ]), ), ], ), ] return cc_common.create_cc_toolchain_config_info( ctx = ctx, features = features, # NEW cxx_builtin_include_directories = [ "/usr/lib/llvm-9/lib/clang/9.0.1/include", "/usr/include", ], toolchain_identifier = "local", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", tool_paths = tool_paths, ) cc_toolchain_config = rule( implementation = _impl, attrs = {}, provides = [CcToolchainConfigInfo], )
Executando
bazel build //main:hello-world
, ele finalmente vai criar o binário para o host.Em
toolchain/BUILD
, copie os destinoscc_toolchain_config
,cc_toolchain
etoolchain
e substitualinux_x86_64
porandroid_x86_64
nos nomes dos destinos.Em
WORKSPACE
, registre o conjunto de ferramentas para Android.register_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64", "//toolchain:cc_toolchain_for_android_x86_64" )
Execute
bazel build //main:hello-world --android_platforms=//toolchain:android_x86_64
para criar o binário para Android.
Na prática, o Linux e o Android precisam ter configurações diferentes do conjunto de ferramentas em C++. É
possível modificar o cc_toolchain_config
atual para as diferenças ou
criar regras separadas (por exemplo, provedor CcToolchainConfigInfo
) para plataformas
diferentes.
Revisar seu trabalho
Neste tutorial, você aprendeu a configurar um conjunto de ferramentas básico em C++, mas eles são mais eficientes do que este exemplo simples.
As principais conclusões são:
- Você precisa especificar uma sinalização
platforms
correspondente na linha de comando para que o Bazel resolva no conjunto de ferramentas para os mesmos valores de restrição na plataforma. A documentação contém mais informações sobre sinalizações de configuração de linguagens específicas. - Você precisa informar ao conjunto de ferramentas onde ficam as ferramentas. Neste tutorial, há uma versão simplificada em que você acessa as ferramentas do sistema. Se você tiver interesse em uma abordagem mais independente, leia sobre espaços de trabalho. Suas ferramentas podem vir de um
espaço de trabalho diferente, e você precisaria disponibilizar os arquivos
para o
cc_toolchain
com dependências de destino em atributos, comocompiler_files
. Otool_paths
também precisa ser modificado. - É possível criar recursos para personalizar quais sinalizações precisam ser transmitidas para diferentes ações, sejam elas vinculação ou qualquer outro tipo de ação.
Leia mais
Para ver mais detalhes, consulte Configuração do conjunto de ferramentas do C++.