En este instructivo, se usa una situación de ejemplo a fin de describir cómo configurar las cadenas de herramientas de C++ para un proyecto. Se basa en un proyecto de C++ de ejemplo que compila sin errores mediante clang
.
Qué aprenderás
En este instructivo, aprenderás a realizar las siguientes acciones:
- Cómo configurar el entorno de compilación
- Configura la cadena de herramientas de C++
- Crea una regla de Starlark que proporcione una configuración
adicional para
cc_toolchain
a fin de que Bazel pueda compilar la aplicación conclang
- Ejecuta
bazel build --config=clang_config //main:hello-world
en una máquina Linux para confirmar el resultado esperado. - Compila la aplicación C++
Antes de comenzar
Cómo configurar el entorno de compilación
En este instructivo, se supone que estás en Linux, que compilaste aplicaciones de C++ y, luego, instalaste las herramientas y bibliotecas correspondientes correctamente.
En el 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 hiciste, descarga y, luego, 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 de lugar de trabajo con los siguientes contenidos 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á asociada con esa marca en particular. Consulta la documentación para ver las marcas utilizadas: crosstool_top
, cpu
y host_crosstool_top
.
Cuando compilas tu destino
con bazel build --config=clang_config //main:hello-world
, Bazel usa tu
cadena de herramientas personalizada de la
cc_toolchain_suite
//toolchain:clang_suite
. El paquete puede enumerar diferentes cadenas de herramientas para diferentes CPU, por lo que se diferencia de la marca --cpu=k8
.
Debido a que Bazel usa muchas herramientas internas escritas en C++ durante la compilación, como un wrapper del proceso, se especifica la cadena de herramientas de C++ predeterminada preexistente para la plataforma host, a fin de que estas herramientas se compilen mediante esa cadena en lugar de la que se creó en este instructivo.
Configura la cadena de herramientas de C++
Para configurar la cadena de herramientas de C++, compila repetidamente la aplicación 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 el archivo.bazelrc
, Bazel arroja el siguiente error:No such package `toolchain`: BUILD file not found on package path.
En el directorio de 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 el destinoclang_suite
, Bazel muestra 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 muestra 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 el proveedorToolchainInfo
necesario. Por lo tanto, debes apuntar--crosstool_top
a una regla que proporcioneToolchainInfo
, que es la reglacc_toolchain_suite
. En el archivotoolchain/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 los valores--cpu
(y también--compiler
cuando se especifica) acc_toolchain
. Todavía no definiste ningún destinocc_toolchain
, y Bazel se quejará de eso pronto.Vuelve a ejecutar la compilación. Bazel muestra el siguiente error:
Rule '//toolchain:k8_toolchain' does not exist
Ahora, debes definir objetivos
cc_toolchain
para cada valor del atributocc_toolchain_suite.toolchains
. Agrega lo siguiente 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 muestra el siguiente error:
Rule '//toolchain:k8_toolchain_config' does not exist
A continuación, agrega un destino ":k8_toolchain_config" al archivo
toolchain/BUILD
:filegroup(name = "k8_toolchain_config")
Vuelve a ejecutar la compilación. Bazel muestra el siguiente error:
'//toolchain:k8_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'
CcToolchainConfigInfo
es un proveedor que usas para configurar tus cadenas de herramientas de C++. Para corregir este error, crea una regla de Starlark que proporcioneCcToolchainConfigInfo
a Bazel mediante la creación de un archivotoolchain/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 proveedorCcToolchainConfigInfo
necesario. Para usar la reglacc_toolchain_config
, agrega una declaración de carga atoolchain/BUILD
justo debajo de la instrucción del paquete:load(":cc_toolchain_config.bzl", "cc_toolchain_config")
Y reemplaza el grupo de archivos “k8_toolchain_config” con una declaración de una regla
cc_toolchain_config
:cc_toolchain_config(name = "k8_toolchain_config")
Vuelve a ejecutar la compilación. Bazel muestra 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 aún no sabe qué herramientas usar a fin de completar las acciones de compilación requeridas. Modificarás la implementación de la regla de Starlark para indicarle a Bazel qué herramientas usar. Para eso, necesitas el constructor de 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 de acceso correctas para tu sistema.Vuelve a ejecutar la compilación. Bazel muestra 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 solucionar este problema, como usar el atributo
includes
decc_binary
, pero aquí se resuelve a nivel de la cadena de herramientas con el parámetrocxx_builtin_include_directories
decc_common.create_cc_toolchain_config_info
. Ten en cuenta que, si usas una versión diferente declang
, la ruta de inclusión será 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 de la siguiente manera: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 y 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 la biblioteca estándar de C++ y no puede encontrar sus símbolos. Hay muchas formas de solucionar este problema, como usar el atributo
linkopts
decc_binary
. Aquí se resuelve asegurándose de que cualquier objetivo que use la cadena de herramientas no tenga 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
, finalmente debería compilarse.
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 potentes que en este ejemplo simple.
Las conclusiones clave son las siguientes:
- Debes especificar una marca --crosstool_top
en la línea de comandos que debe apuntar a un cc_toolchain_suite
.
- Puedes crear un acceso directo para una configuración específica mediante el archivo .bazelrc
.
- La cc_toolchain_suite puede enumerar cc_toolchains
para diferentes CPU y
compiladores. Puedes usar marcas de línea de comandos como --cpu
para diferenciarlas.
- Debes informar a la cadena de herramientas dónde se encuentran las herramientas. En este instructivo, hay una versión simplificada en la que puedes acceder a las herramientas desde el sistema. Si te interesa un enfoque más independiente, puedes obtener información sobre los lugares de trabajo aquí. Tus herramientas podrían provenir de un lugar de trabajo diferente y tendrías que hacer que sus archivos estén disponibles para el cc_toolchain
con dependencias de destino en los atributos, como compiler_files
. También se debe cambiar el tool_paths
.
- Puedes crear funciones para personalizar las marcas que se deben pasar a diferentes acciones, ya sean vinculaciones o cualquier otro tipo de acción.
Lecturas adicionales
Para obtener más detalles, consulta la configuración de la cadena de herramientas de C++.