Este tutorial usa um cenário de exemplo para descrever como configurar 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_debugpara depurar a resolução do conjunto de ferramentas - Configurar o conjunto de ferramentas C++
- Criar uma regra do Starlark que forneça configuração extra para o
cc_toolchain, para que o Bazel possa criar o aplicativo comclang - Criar o binário C++ executando
bazel build //main:hello-worldem uma máquina Linux - Fazer 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 criou aplicativos C++ e instalou as ferramentas e bibliotecas adequadas. O tutorial
usa clang version 19, que pode ser instalado no seu sistema.
Configurar o ambiente de build
Configure o ambiente de build da seguinte maneira:
Faça o download e a instalação do Bazel 7.0.2 ou mais recente, se ainda não tiver feito isso.
Adicione um arquivo
MODULE.bazelvazio na pasta raiz.Adicione o destino
cc_binarya seguir ao arquivomain/BUILD:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )Como o Bazel usa muitas ferramentas internas escritas em C++ durante o build, como
process-wrapper, o conjunto de ferramentas C++ padrão pré-existente é especificado para a plataforma do host. Isso permite que essas ferramentas internas sejam criadas usando esse conjunto de ferramentas do criado neste tutorial. Portanto, o destinocc_binarytambém é criado com o conjunto de ferramentas padrão.Execute o build com o seguinte comando:
bazel build //main:hello-worldO build é bem-sucedido sem nenhum conjunto de ferramentas registrado em
MODULE.bazel.Para saber mais sobre o que está acontecendo, execute:
bazel build //main:hello-world --toolchain_resolution_debug='@bazel_tools//tools/cpp:toolchain_type' INFO: ToolchainResolution: Target platform @@platforms//host:host: Selected execution platform @@platforms//host:host, type @@bazel_tools//tools/cpp:toolchain_type -> toolchain @@bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8Sem especificar
--platforms, o Bazel cria o destino para@platforms//hostusando@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 abaixo.
Ele também pressupõe clang version 9.0.1, embora os detalhes só mudem
um pouco entre as diferentes versões do clang.
Adicione
toolchain/BUILDcomfilegroup(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, adicione as dependências adequadas e registre o conjunto de ferramentas com
MODULE.bazelcombazel_dep(name = "platforms", version = "0.0.10") register_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64" )Esta etapa define um
cc_toolchaine o vincula a um destinotoolchainpara a configuração do host.Execute o build novamente. Como o pacote
toolchainainda não define olinux_x86_64_toolchain_configdestino, 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 filegroup 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 seus conjuntos de ferramentas C++. Para corrigir esse erro, crie uma regra do Starlark que forneçaCcToolchainConfigInfoao Bazel criando um arquivotoolchain/cc_toolchain_config.bzlcom 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/BUILDlogo abaixo da instrução do pacote:load(":cc_toolchain_config.bzl", "cc_toolchain_config")E substitua o filegroup "linux_x86_64_toolchain_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`Nesse momento, 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ê vai 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", # Compiler is referenced by the name "gcc" for historic reasons. 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 )Verifique se
/usr/bin/clange/usr/bin/ldsão os caminhos corretos para o seu sistema. O compilador é referenciado pelo nome "gcc" por motivos históricos.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 os cabeçalhos incluídos. Há várias maneiras de resolver isso, como usar o atributo
includesdecc_binary, mas aqui isso é resolvido no nível do conjunto de ferramentas com ocxx_builtin_include_directoriesparâmetro decc_common.create_cc_toolchain_config_info. Se você estiver usando uma versão diferente doclang, 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.bzlpara que ele fique assim:return cc_common.create_cc_toolchain_config_info( ctx = ctx, cxx_builtin_include_directories = [ # NEW "/usr/lib/llvm-19/lib/clang/19/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. Você vai receber um erro como:
/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 é que o vinculador não tem a biblioteca padrão C++ e não consegue encontrar os símbolos dela. Há muitas maneiras de resolver isso, como usar o atributo
linkoptsdecc_binary. Aqui, isso é resolvido garantindo que nenhum destino que use o conjunto de ferramentas precise especificar essa flag.Copie o código a seguir 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", # Compiler is referenced by the name "gcc" for historic reasons. 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-19/lib/clang/19/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], )Esse código usa a biblioteca GNU C++ libstdc++. Se você quiser usar a biblioteca LLVM C++, use "-lc++" em vez de "-lstdc++".
Executar
bazel build //main:hello-worlddeve finalmente criar o binário com sucesso para o host.Em
toolchain/BUILD, copie os destinoscc_toolchain_config,cc_toolchain, etoolchaine substitualinux_x86_64porandroid_x86_64em nomes de destino.Em
MODULE.bazel, registre o conjunto de ferramentas para Androidregister_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_64para criar o binário para Android.
Na prática, o Linux e o Android precisam ter configurações diferentes de conjunto de ferramentas C++. Você pode modificar o cc_toolchain_config atual para as diferenças ou criar regras separadas (ou seja, provedor CcToolchainConfigInfo) para plataformas separadas.
Revisar seu trabalho
Neste tutorial, você aprendeu a configurar um conjunto de ferramentas C++ básico, mas os conjuntos de ferramentas são mais poderosos do que este exemplo.
Os principais tópicos são:
- É necessário especificar uma flag
platformscorrespondente na linha de comando para que o Bazel resolva o conjunto de ferramentas para os mesmos valores de restrição na plataforma. A documentação contém mais informações sobre flags de configuração específicas do idioma. - Você precisa informar ao conjunto de ferramentas onde as ferramentas estão. Neste tutorial, há uma versão simplificada em que você acessa as ferramentas do sistema. Se
você estiver interessado em uma abordagem mais independente, leia sobre
dependências externas. Suas ferramentas podem vir de um módulo diferente, e você precisará disponibilizar os arquivos delas para o
cc_toolchaincom dependências de destino em atributos, comocompiler_files. Ostool_pathstambém precisam ser alterados. - Você pode criar recursos para personalizar quais flags precisam ser transmitidas para diferentes ações, seja vinculação ou qualquer outro tipo de ação.
Leitura adicional
Para mais detalhes, consulte Configuração do conjunto de ferramentas C++