Este tutorial usa um cenário de exemplo para descrever como configurar conjuntos de ferramentas C++
para um projeto. Ele é baseado em um
projeto de exemplo em C++
que cria sem erros usando clang.
O que você vai aprender
Neste tutorial, você vai aprender a:
- Configurar o ambiente de build
- Configurar o conjunto de ferramentas C++
- Criar uma regra do Starlark que forneça configuração adicional
para o
cc_toolchain, para que o Bazel possa criar o aplicativo comclang - Confirmar o resultado esperado executando
bazel build --config=clang_config //main:hello-worldem uma máquina Linux - Criar o aplicativo C++
Antes de começar
Configurar o ambiente de build
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 9.0.1, que pode ser instalado no seu sistema.
Configure o ambiente de build da seguinte maneira:
Faça o download e instale o Bazel 0.23 ou mais recente, caso ainda não tenha feito isso.
Faça o download do projeto de exemplo em C++ do GitHub e coloque-o em um diretório vazio na sua máquina local.
Adicione o destino
cc_binarya seguir ao arquivomain/BUILD:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )Crie um arquivo
.bazelrcna raiz do diretório do espaço de trabalho com o conteúdo a seguir para ativar o uso da flag--config:# Use our custom-configured c++ toolchain. build:clang_config --crosstool_top=//toolchain:clang_suite # Use --cpu as a differentiator. build:clang_config --cpu=k8 # Use the default Bazel C++ toolchain to build the tools used during the # build. build:clang_config --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
Para uma entrada build:{config_name} --flag=value, a flag de linha de comando
--config={config_name} está associada a essa flag específica. Consulte
a documentação das flags usadas:
crosstool_top,
cpu e
host_crosstool_top.
Ao criar seu destino
com bazel build --config=clang_config //main:hello-world, o Bazel usa seu
conjunto de ferramentas personalizado do
cc_toolchain_suite
//toolchain:clang_suite. O conjunto pode listar
conjuntos de ferramentas diferentes para CPUs diferentes,
e é por isso que ele é diferenciado com a flag --cpu=k8.
Como o Bazel usa muitas ferramentas internas escritas em C++ durante o build, como o wrapper de processo, o conjunto de ferramentas C++ padrão preexistente é especificado para a plataforma do host, para que essas ferramentas sejam criadas usando esse conjunto de ferramentas em vez de o criado neste tutorial.
Como 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.
Execute o build com o seguinte comando:
bazel build --config=clang_config //main:hello-worldComo você especificou
--crosstool_top=//toolchain:clang_suiteno.bazelrcarquivo, o Bazel gera o seguinte erro:No such package `toolchain`: BUILD file not found on package path.No diretório do espaço de trabalho, crie o diretório
toolchainpara o pacote e um arquivoBUILDvazio dentro do diretóriotoolchain.Execute o build novamente. Como o pacote
toolchainainda não define oclang_suitedestino, o Bazel gera o seguinte erro:No such target '//toolchain:clang_suite': target 'clang_suite' not declared in package 'toolchain' defined by .../toolchain/BUILDNo arquivo
toolchain/BUILD, defina um filegroup vazio da seguinte maneira:package(default_visibility = ["//visibility:public"]) filegroup(name = "clang_suite")Execute o build novamente. O Bazel gera o seguinte erro:
'//toolchain:clang_suite' does not have mandatory providers: 'ToolchainInfo'O Bazel descobriu que a flag
--crosstool_topaponta para uma regra que não fornece o provedorToolchainInfonecessário. Portanto, é necessário apontar--crosstool_toppara uma regra que forneçaToolchainInfo- que é a regracc_toolchain_suite. No arquivotoolchain/BUILD, substitua o filegroup vazio pelo seguinte:cc_toolchain_suite( name = "clang_suite", toolchains = { "k8": ":k8_toolchain", }, )O atributo
toolchainsmapeia automaticamente os valores--cpu(e também--compilerquando especificado) paracc_toolchain. Você ainda não definiu nenhum destinocc_toolchain, e o Bazel vai reclamar disso em breve.Execute o build novamente. O Bazel gera o seguinte erro:
Rule '//toolchain:k8_toolchain' does not existAgora você precisa definir
cc_toolchaindestinos para cada valor nocc_toolchain_suite.toolchainsatributo. Adicione o seguinte ao arquivotoolchain/BUILD:filegroup(name = "empty") cc_toolchain( name = "k8_toolchain", toolchain_identifier = "k8-toolchain", toolchain_config = ":k8_toolchain_config", all_files = ":empty", compiler_files = ":empty", dwp_files = ":empty", linker_files = ":empty", objcopy_files = ":empty", strip_files = ":empty", supports_param_files = 0, )Execute o build novamente. O Bazel gera o seguinte erro:
Rule '//toolchain:k8_toolchain_config' does not existEm seguida, adicione um destino ":k8_toolchain_config" ao arquivo
toolchain/BUILD:filegroup(name = "k8_toolchain_config")Execute o build novamente. O Bazel gera o seguinte erro:
'//toolchain:k8_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 "k8_toolchain_config" por uma declaração de uma regra
cc_toolchain_config:cc_toolchain_config(name = "k8_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", 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 sistema.Execute o build novamente. O Bazel gera o seguinte erro:
..../BUILD:3:1: undeclared inclusion(s) in rule '//main:hello-world': this rule is missing dependency declarations for the following files included by 'main/hello-world.cc': '/usr/include/c++/9/ctime' '/usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h' '/usr/include/x86_64-linux-gnu/c++/9/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
includesatributo decc_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-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, )Execute o comando de build novamente. Você vai encontrar 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 está ausente da biblioteca padrão do C++ e não consegue encontrar os símbolos. 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
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", "flag_group", "flag_set", "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], )Se você executar
bazel build --config=clang_config //main:hello-world, ele vai ser criado.
Analisar 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 simples.
As principais conclusões são:
- É necessário especificar uma flag --crosstool_top na linha de comando que aponte
para um cc_toolchain_suite
- É possível criar um atalho para uma configuração específica usando o arquivo.bazelrc
- O cc_toolchain_suite pode listar cc_toolchains para diferentes CPUs e
compiladores. Você pode usar flags de linha de comando como --cpu para diferenciar.
- É necessário 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ê tiver interesse em uma abordagem mais independente, leia sobre
espaços de trabalho aqui. Suas ferramentas podem vir de um
espaço de trabalho diferente, e você precisaria disponibilizar os arquivos delas
para o cc_toolchain com dependências de destino em atributos, como
compiler_files. O tool_paths também precisaria ser alterado.
- É possível 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++.