En este instructivo, se usa una situación de ejemplo para describir cómo configurar cadenas de herramientas C++ para un proyecto.
Qué aprenderás
En este instructivo, aprenderás a hacer lo siguiente:
- Cómo configurar el entorno de compilación
- Usa
--toolchain_resolution_debug
para depurar la resolución de la cadena de herramientas - Cómo configurar la cadena de herramientas de C++
- Crea una regla de Starlark que proporcione una configuración adicional para
cc_toolchain
de modo que Bazel pueda compilar la aplicación conclang
. - Ejecuta
bazel build //main:hello-world
en una máquina Linux para compilar el objeto binario de C++. - Ejecuta
bazel build //main:hello-world --platforms=//:android_x86_64
para compilar de forma cruzada el objeto binario para Android.
Antes de comenzar
En este instructivo, se supone que estás en Linux y que compilaste aplicaciones de C++ y que instalaste las herramientas y bibliotecas adecuadas.
En este instructivo, se usa clang version 16
, que puedes instalar en tu sistema.
Cómo configurar el entorno de compilación
Configura tu entorno de compilación de la siguiente manera:
Si aún no lo hiciste, descarga y, luego, instala Bazel 7.0.2 o una versión posterior.
Agrega un archivo
WORKSPACE
vacío en la carpeta raíz.Agrega el siguiente destino
cc_binary
al archivomain/BUILD
:load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
Debido a que Bazel usa muchas herramientas internas escritas en C++ durante la compilación, como
process-wrapper
, se especifica la cadena de herramientas de C++ predeterminada preexistente para la plataforma host. De esta manera, se permite que estas herramientas internas compilen con la cadena de herramientas que creaste en este instructivo. Por lo tanto, el objetivocc_binary
también se compila con la cadena de herramientas predeterminada.Ejecuta la compilación con el siguiente comando:
bazel build //main:hello-world
La compilación se realiza de forma correcta sin ninguna cadena de herramientas registrada en
WORKSPACE
.Para ver mejor lo que hay debajo, ejecuta el siguiente comando:
bazel build //main:hello-world --toolchain_resolution_debug='@bazel_tools//tools/cpp:toolchain_type' INFO: ToolchainResolution: Target platform @@local_config_platform//:host: Selected execution platform @@local_config_platform//:host, type @@bazel_tools//tools/cpp:toolchain_type -> toolchain @@bazel_tools~cc_configure_extension~local_config_cc//:cc-compiler-k8
Sin especificar
--platforms
, Bazel compila el destino para@local_config_platform//:host
mediante@bazel_tools//cc_configure_extension/local_config_cc//:cc-compiler-k8
.
Cómo configurar la cadena de herramientas de C++
Para configurar la cadena de herramientas de C++, compila la aplicación repetidamente y elimina cada error uno por uno, como se describe a continuación.
También supone clang version 9.0.1
, aunque los detalles solo deben cambiar ligeramente entre las diferentes versiones de clang.
Agregar
toolchain/BUILD
confilegroup(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", ], )
Luego, registra la cadena de herramientas con
WORKSPACE
con elregister_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64" )
En este paso, se define un
cc_toolchain
y se lo vincula a un destinotoolchain
para la configuración del host.Vuelve a ejecutar la compilación. Debido a que el paquete
toolchain
aún no define el destinolinux_x86_64_toolchain_config
, Bazel arroja el siguiente error: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.
En el archivo
toolchain/BUILD
, define un grupo de archivos vacío de la siguiente manera:package(default_visibility = ["//visibility:public"]) filegroup(name = "linux_x86_64_toolchain_config")
Vuelve a ejecutar la compilación. Bazel arroja el siguiente error:
'//toolchain:linux_x86_64_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 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 proveedor necesarioCcToolchainConfigInfo
. Para usar la reglacc_toolchain_config
, agrega una declaración de carga atoolchain/BUILD
justo debajo de la declaración del paquete:load(":cc_toolchain_config.bzl", "cc_toolchain_config")
Además, reemplaza el grupo de archivos "linux_x86_64_toolchain_config" por una declaración de regla
cc_toolchain_config
:cc_toolchain_config(name = "linux_x86_64_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 aún no sabe qué herramientas usar para completar las acciones de compilación necesarias. Modificarás la implementación de la regla de Starlark para indicarle a Bazel qué herramientas usar. Para ello, 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 de acceso correctas para tu sistema.Vuelve a ejecutar la compilación. Bazel arroja el siguiente error:
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' ...
Bazel necesita saber dónde buscar los encabezados incluidos. Hay varias formas de resolver esto, 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 acceso 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-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, )
Ejecuta el comando de compilación nuevamente 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 C++ estándar y no puede encontrar sus símbolos. Existen muchas formas de resolver esto, como usar el atributo
linkopts
decc_binary
. Aquí, se resuelve garantizando que los destinos que usan la cadena de herramientas no tengan que especificar esta marca.Copia el siguiente código en
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], )
Ejecutando
bazel build //main:hello-world
, finalmente debería compilar el objeto binario de forma correcta para el host.En
toolchain/BUILD
, copia los destinoscc_toolchain_config
,cc_toolchain
ytoolchain
, y reemplazalinux_x86_64
porandroid_x86_64
en los nombres de destino.En
WORKSPACE
, registra la cadena de herramientas para Androidregister_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64", "//toolchain:cc_toolchain_for_android_x86_64" )
Ejecuta
bazel build //main:hello-world --android_platforms=//toolchain:android_x86_64
para compilar el objeto binario para Android.
En la práctica, Linux y Android deben tener diferentes configuraciones de la cadena de herramientas de C++. Puedes modificar el cc_toolchain_config
existente para las diferencias o crear reglas separadas (es decir, un proveedor de CcToolchainConfigInfo
) para plataformas distintas.
Revisa tu trabajo
En este instructivo, aprendiste a configurar una cadena de herramientas básica de C++, pero estas son más potentes que este ejemplo sencillo.
Los puntos clave son los siguientes:
- Debes especificar una marca
platforms
coincidente en la línea de comandos para que Bazel se resuelva en la cadena de herramientas de los mismos valores de restricción en la plataforma. En la documentación, se incluye más información sobre las marcas de configuración específicas de cada lenguaje. - 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 accedes a las herramientas desde el sistema. Si te interesa un enfoque más autónomo, consulta los lugares de trabajo. Las herramientas podrían provenir de un lugar de trabajo diferente, y tendrías que hacer que sus archivos estén disponibles para
cc_toolchain
con dependencias objetivo en los atributos, comocompiler_files
. También se deberá cambiar eltool_paths
. - Puedes crear funciones para personalizar qué marcas se deben pasar a diferentes acciones, ya sea de vinculación o cualquier otro tipo de acción.
Lecturas adicionales
Para obtener más detalles, consulta Configuración de la cadena de herramientas C++