En este instructivo, se usa una situación de ejemplo para describir cómo configurar C++
cadenas de herramientas para un proyecto. Se basa en un
proyecto de ejemplo de C++
que se compile sin errores con clang
.
Qué aprenderás
En este instructivo, aprenderás a hacer lo siguiente:
- Cómo configurar el entorno de compilación
- Cómo configurar la cadena de herramientas de C++
- Crea una regla de Starlark que proporcione
configuración para
cc_toolchain
de modo que Bazel pueda compilar la aplicación. conclang
- Ejecuta la app para confirmar el resultado esperado
bazel build --config=clang_config //main:hello-world
en una máquina Linux - Compila la aplicación de C++
Antes de comenzar
Cómo configurar el entorno de compilación
En este instructivo, se supone que usas Linux y que compilaste correctamente
C++ y, además, instalaron las herramientas y bibliotecas correspondientes.
En este instructivo, se usa clang version 9.0.1
, que puedes instalar en tu sistema.
Configura tu entorno de compilación de la siguiente manera:
Si aún no lo has hecho, descarga e instala Bazel 0.23 o una versión posterior.
Descarga el proyecto de ejemplo de C++ de GitHub y colócalo en un directorio vacío en tu máquina local.
Agrega el siguiente destino
cc_binary
al archivomain/BUILD
:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
Crea un archivo
.bazelrc
en la raíz del directorio del espacio de trabajo con el el siguiente contenido para habilitar el uso de la marca--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 una entrada build:{config_name} --flag=value
, la marca de línea de comandos
--config={config_name}
está asociado con esa marca en particular. Consulta
documentación para las marcas usadas:
crosstool_top
,
cpu
y
host_crosstool_top
Cuando estableces tu objetivo
con bazel build --config=clang_config //main:hello-world
, Bazel usa tu
personalizada
cc_toolchain_suite
//toolchain:clang_suite
Es posible que el paquete muestre diferentes
cadenas de herramientas para diferentes CPU,
y es por eso que se diferencia con la marca --cpu=k8
.
Debido a que Bazel usa muchas herramientas internas escritas en C++ durante la compilación, como las siguientes: process-wrapper, se especifica la cadena de herramientas predeterminada de C++ preexistente para la plataforma host, de modo que estas herramientas se compilen usando esa cadena de herramientas en lugar de la que se creó en este instructivo.
Cómo configurar la cadena de herramientas de C++
Para configurar la cadena de herramientas de C++, compila la aplicación de forma repetida y elimina cada error uno por uno, como se describe a continuación.
Ejecuta la compilación con el siguiente comando:
bazel build --config=clang_config //main:hello-world
Debido a que especificaste
--crosstool_top=//toolchain:clang_suite
en.bazelrc
, Bazel arroja el siguiente error:No such package `toolchain`: BUILD file not found on package path.
En el directorio del espacio de trabajo, crea el directorio
toolchain
para el paquete. y un archivoBUILD
vacío dentro del directoriotoolchain
.Vuelve a ejecutar la compilación. Debido a que el paquete
toolchain
aún no define elclang_suite
, Bazel arroja el siguiente error:No such target '//toolchain:clang_suite': target 'clang_suite' not declared in package 'toolchain' defined by .../toolchain/BUILD
En el archivo
toolchain/BUILD
, define un grupo de archivos vacío de la siguiente manera:package(default_visibility = ["//visibility:public"]) filegroup(name = "clang_suite")
Vuelve a ejecutar la compilación. Bazel arroja el siguiente error:
'//toolchain:clang_suite' does not have mandatory providers: 'ToolchainInfo'
Bazel descubrió que la marca
--crosstool_top
apunta a una regla que no proporciona losToolchainInfo
necesarios proveedor. Por lo tanto, debes apuntar--crosstool_top
a una regla que proporcioneToolchainInfo
: esa es la reglacc_toolchain_suite
. En latoolchain/BUILD
, reemplaza el grupo de archivos vacío por lo siguiente:cc_toolchain_suite( name = "clang_suite", toolchains = { "k8": ":k8_toolchain", }, )
El atributo
toolchains
asigna automáticamente el--cpu
(y también--compiler
cuando se especifica) comocc_toolchain
. Aún no definió cualquier objetivocc_toolchain
, y Bazel se quejará de eso. en poco tiempo.Vuelve a ejecutar la compilación. Bazel arroja el siguiente error:
Rule '//toolchain:k8_toolchain' does not exist
Ahora debes definir objetivos
cc_toolchain
para cada valor en elcc_toolchain_suite.toolchains
. Agrega la siguiente información al Archivotoolchain/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, )
Vuelve a ejecutar la compilación. Bazel arroja el siguiente error:
Rule '//toolchain:k8_toolchain_config' does not exist
A continuación, agrega un valor de “:k8_toolchain_config” se orienta al archivo
toolchain/BUILD
:filegroup(name = "k8_toolchain_config")
Vuelve a ejecutar la compilación. Bazel arroja el siguiente error:
'//toolchain:k8_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'
CcToolchainConfigInfo
es un proveedor que se usa para configurar de tus cadenas de herramientas de C++. Para corregir este error, crea una regla de Starlark que proporcionaCcToolchainConfigInfo
a Bazel haciendo untoolchain/cc_toolchain_config.bzl
con el siguiente contenido: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()
crea el proveedor necesario.CcToolchainConfigInfo
Para usar la reglacc_toolchain_config
, agrega una carga atoolchain/BUILD
justo debajo de la declaración del paquete:load(":cc_toolchain_config.bzl", "cc_toolchain_config")
Y reemplaza "k8_toolchain_config" de un grupo de archivos con la declaración del
cc_toolchain_config
regla:cc_toolchain_config(name = "k8_toolchain_config")
Vuelve a ejecutar la compilación. Bazel arroja el siguiente error:
.../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`
En este punto, Bazel tiene suficiente información para intentar compilar el código, pero todavía no sabe qué herramientas usar para completar la compilación necesaria acciones. Modificarás la implementación de la regla de Starlark para indicarle a Bazel qué. herramientas para usar. Para eso, necesitas el constructor 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 )
Asegúrate de que
/usr/bin/clang
y/usr/bin/ld
sean las rutas correctas para tu sistema.Vuelve a ejecutar la compilación. Bazel arroja el siguiente error:
..../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' ....
Bazel necesita saber dónde buscar los encabezados incluidos. Existen varias formas de resolver esto, como usar el atributo
includes
decc_binary
, pero aquí esto se resuelve a nivel de la cadena de herramientas con elcxx_builtin_include_directories
parámetro decc_common.create_cc_toolchain_config_info
. Ten en cuenta que si estás usando una versión diferente declang
, se agregará la ruta de acceso de inclusión es diferente. Estas rutas también pueden ser diferentes según la distribución.Modifica el valor que se muestra en
toolchain/cc_toolchain_config.bzl
para que se vea. así: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, )
Vuelve a ejecutar el comando de compilación. Verás un error como el siguiente:
/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'
Esto se debe a que al vinculador le falta el estándar C++ biblioteca y no encuentra sus símbolos. Hay muchas formas de resolver esto, como el uso del atributo
linkopts
decc_binary
. Aquí se resuelve con asegúrate de que los destinos que usan la cadena de herramientas no tengan que especificar esta marca.Copia el siguiente código en
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], )
Si ejecutas
bazel build --config=clang_config //main:hello-world
, debería aparecer finalmente construir.
Revisa tu trabajo
En este instructivo, aprendiste a configurar una cadena de herramientas básica de C++, pero Las cadenas de herramientas son más poderosas que este ejemplo simple.
Los puntos clave son los siguientes:
- Debes especificar una marca --crosstool_top
en la línea de comandos que debería
señala un cc_toolchain_suite
- Puedes crear un acceso directo para una configuración en particular usando .bazelrc
.
archivo
- cc_toolchain_suite podría indicar cc_toolchains
para diferentes CPU
compiladores. Puedes usar marcas de línea de comandos, como --cpu
, para diferenciarlos.
- Debes permitir que la cadena de herramientas sepa dónde se encuentran las herramientas. En este instructivo
hay una versión simplificada
en la que se accede a las herramientas del sistema. Si
estás interesado en un enfoque más autónomo, puedes leer acerca de
espacios de trabajo aquí. Tus herramientas podrían provenir de un
espacio de trabajo diferente y tendrías que hacer que sus archivos estén disponibles
a cc_toolchain
con dependencias de destino en atributos, como
compiler_files
También se debería cambiar tool_paths
.
- Puedes crear funciones para personalizar a qué marcas se deben pasar
diferentes acciones, ya sea la vinculación o cualquier otro tipo de acción.
Lecturas adicionales
Para obtener más detalles, consulta Configuración de la cadena de herramientas de C++