Este tutorial usa um cenário de exemplo para descrever como configurar C++
de última geração para um projeto. Ela é baseada em uma
exemplo de projeto 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 do C++
- Crie uma regra Starlark que forneça mais
configuração 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-world
em uma máquina Linux - Criar o aplicativo em C++
Antes de começar
Configurar o ambiente de build
Neste tutorial, presumimos que você esteja usando o Linux e tenha criado
aplicativos em C++ e instalou as ferramentas e bibliotecas adequadas.
O tutorial usa clang version 9.0.1
, que pode ser instalado no sistema.
Configure o ambiente de build da seguinte forma:
Caso ainda não tenha feito isso, faça o download e instale o Bazel 0.23 ou posterior.
Faça o download do exemplo de projeto em C++ do GitHub e colocá-lo em um diretório vazio na máquina local.
Adicione o seguinte destino
cc_binary
ao arquivomain/BUILD
:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
Crie um arquivo
.bazelrc
na raiz do diretório do espaço de trabalho com o o seguinte conteúdo 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 sinalização de linha de comando
--config={config_name}
está associado a essa sinalização específica. Consulte
documentação das sinalizações usadas:
crosstool_top
,
cpu
e
host_crosstool_top
.
Quando você cria seu destino
com bazel build --config=clang_config //main:hello-world
, o Bazel usa
conjunto de ferramentas personalizado
cc_toolchain_suite
//toolchain:clang_suite
. A suíte pode listar
conjuntos de ferramentas 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 process-wrapper, o conjunto de ferramentas padrão C++ preexistente será especificado para plataforma host, para que essas ferramentas sejam criadas usando esse conjunto de ferramentas em vez de a que foi criada neste tutorial.
Como 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 abaixo.
Execute o build com o seguinte comando:
bazel build --config=clang_config //main:hello-world
Como você especificou
--crosstool_top=//toolchain:clang_suite
no.bazelrc
, 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
toolchain
para o pacote. e um arquivoBUILD
vazio dentro do diretóriotoolchain
.Execute o build novamente. Como o pacote
toolchain
ainda não define oclang_suite
, o Bazel gera o seguinte erro:No such target '//toolchain:clang_suite': target 'clang_suite' not declared in package 'toolchain' defined by .../toolchain/BUILD
No arquivo
toolchain/BUILD
, defina um grupo de arquivos 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_top
aponta para uma regra que não fornece oToolchainInfo
necessário de nuvem. Portanto, é necessário apontar--crosstool_top
para uma regra que forneçaToolchainInfo
: essa é a regracc_toolchain_suite
. Natoolchain/BUILD
, substitua o grupo de arquivos vazio pelo seguinte:cc_toolchain_suite( name = "clang_suite", toolchains = { "k8": ":k8_toolchain", }, )
O atributo
toolchains
mapeia automaticamente o--cpu
(e também--compiler
quando especificado) comocc_toolchain
. Você ainda não definiu quaisquer objetivoscc_toolchain
, e o Bazel reclamará disso em pouco tempo.Execute o build novamente. O Bazel gera o seguinte erro:
Rule '//toolchain:k8_toolchain' does not exist
Agora você precisa definir
cc_toolchain
destinos para cada valor nacc_toolchain_suite.toolchains
. 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 exist
Em seguida, adicione um ":k8_Dataset_config" destino para o 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 que você usa para configurar seus conjuntos de ferramentas C++. Para corrigir esse erro, crie uma regra do Starlark que forneceCcToolchainConfigInfo
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 "k8_Dataset_config" de um grupo de arquivos com uma declaração 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`
Neste ponto, o Bazel tem informações suficientes para tentar criar o código, mas ele 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:
..../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. Existem várias maneiras de resolver isso, como usar o atributo
includes
docc_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 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 procurar 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ê 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 essa sinalização.Copie o código abaixo 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 e criar.
Revise seu trabalho
Neste tutorial, você aprendeu como 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
apontar para um cc_toolchain_suite
- É possível criar um atalho para uma configuração específica usando .bazelrc
.
arquivo
- O cc_Dataset_suite pode listar cc_toolchains
para diferentes CPUs e
compiladores. É possível usar sinalizações de linha de comando como --cpu
para diferenciar.
- É necessário 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
estiver interessado 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ê teria que disponibilizar os arquivos deles
ao cc_toolchain
com dependências de destino em atributos, como
compiler_files
. O tool_paths
também precisa ser modificado.
- Você pode criar recursos para personalizar quais sinalizações serão passadas para
ações diferentes, seja vinculação ou qualquer outro tipo de ação.
Leitura adicional
Para mais detalhes, consulte Configuração do conjunto de ferramentas C++