Este tutorial usa um cenário de exemplo para descrever como configurar C++ de última geração 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 do C++
- Crie uma regra Starlark que forneça configuração adicional 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 um 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ê esteja usando o Linux e tenha criado corretamente o C++
e instalar as ferramentas e bibliotecas adequadas. Tutorial
usa clang version 16
, que pode ser instalado no sistema.
Configurar o ambiente de build
Configure o ambiente de build da seguinte forma:
Faça o download e instale o Bazel, se ainda não tiver feito isso. 7.0.2 ou mais recente.
Adicione um arquivo
MODULE.bazel
vazio na pasta raiz.Adicione o seguinte destino
cc_binary
ao arquivomain/BUILD
:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
Como o Bazel usa muitas ferramentas internas escritas em C++ durante a compilação, como como
process-wrapper
, o conjunto de ferramentas C++ padrão preexistente será especificado para a plataforma de hospedagem. Assim, essas ferramentas internas podem ser criadas usando conjunto de ferramentas do 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
MODULE.bazel
.Para conferir mais detalhes, 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-k8
Sem especificar
--platforms
, o Bazel cria o destino para@platforms//host
em@bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8
Configurar o conjunto de ferramentas do C++
Para configurar o conjunto de ferramentas para 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 devam mudar apenas
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, adicione as dependências apropriadas e registre o conjunto de ferramentas com
MODULE.bazel
combazel_dep(name = "platforms", version = "0.0.10") register_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 olinux_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 que você usa para configurar seu arquivo C++ de última geração. Para corrigir esse erro, crie uma regra do Starlark que forneçaCcToolchainConfigInfo
ao Bazel fazendo uma 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 um carregamento paratoolchain/BUILD
logo abaixo da instrução do pacote:load(":cc_toolchain_config.bzl", "cc_toolchain_config")
E substitua "linux_x86_64_Dataset_config" grupo de arquivos com 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 a compilação necessária ações. Você vai modificar a implementação da regra Starlark para informar ao Bazel o que usar. Para isso, você precisa do construtor
tool_path()
da@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 )
Verifique se
/usr/bin/clang
e/usr/bin/ld
são os caminhos corretos para seu 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 os cabeçalhos incluídos. Existem várias maneiras de resolver isso, como usar o atributo
includes
decc_binary
; mas aqui isso é resolvido no nível do conjunto de ferramentas com ocxx_builtin_include_directories
decc_common.create_cc_toolchain_config_info
. Esteja ciente de que, se você estiverem 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: isso: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. Você verá um erro como este:
/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 disso é que o vinculador não tem o padrão C++. e não consegue encontrar os símbolos. Há muitas maneiras de resolver isso, como o uso do atributo
linkopts
decc_binary
. Aqui é resolvido pela garantindo que qualquer destino que use o conjunto de ferramentas não precise especificar isso .Copie o código abaixo 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], )
Ao executar o
bazel build //main:hello-world
, ele finalmente criará o binário. para o host.Em
toolchain/BUILD
, copiecc_toolchain_config
,cc_toolchain
etoolchain
destinos e substituirlinux_x86_64
porandroid_x86_64
em nomes de destino.Em
MODULE.bazel
, 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 de conjunto de ferramentas C++ diferentes. Você
pode modificar o cc_toolchain_config
existente conforme as diferenças ou
criar uma regra separada (por exemplo, CcToolchainConfigInfo
provedor) para
plataformas.
Revise seu trabalho
Neste tutorial, você aprendeu como configurar um conjunto de ferramentas C++ básico, mas conjuntos de ferramentas são mais poderosos do que este exemplo.
Os principais tópicos são:
- É preciso especificar uma sinalização
platforms
correspondente na linha de comando para o Bazel é resolvido para o conjunto de ferramentas para os mesmos valores de restrição no de plataforma. A documentação contém mais informações sobre recursos de configuração do Terraform. - Você precisa permitir que o conjunto de ferramentas saiba onde as ferramentas estão. Neste tutorial
há uma versão simplificada em que você acessa as ferramentas do sistema. Se
se você estiver interessado em uma abordagem mais independente, leia sobre
dependências externas. Suas ferramentas podem vir de um
em cada módulo, e você teria que disponibilizar os arquivos deles
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 flags serão passadas para ações diferentes, seja vinculação ou qualquer outro tipo de ação.
Leitura adicional
Para mais detalhes, consulte Conjunto de ferramentas C++ configuração