En este instructivo, se usa una situación de ejemplo para describir cómo configurar cadenas de herramientas de C++ para un proyecto. Se basa en un
proyecto de ejemplo de C++
que se compila sin errores con clang.
Qué aprenderás
En este instructivo, aprenderás a realizar lo siguiente:
- Configurar el entorno de compilación
- Configurar la cadena de herramientas de C++
- Crear una regla de Starlark que proporcione configuración adicional para
cc_toolchainde modo que Bazel pueda compilar la aplicación conclang - Confirmar el resultado esperado ejecutando
bazel build --config=clang_config //main:hello-worlden una máquina Linux - Compilar la aplicación de C++
Antes de comenzar
Configurar el entorno de compilación
En este instructivo, se supone que usas Linux y que compilaste correctamente aplicaciones de C++ y que instalaste las herramientas y bibliotecas adecuadas.
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 e instala Bazel 0.23 o una versión posterior.
Descarga el proyecto de ejemplo de C++ desde GitHub y colócalo en un directorio vacío de tu máquina local.
Agrega el siguiente destino
cc_binaryal archivomain/BUILD:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )Crea un archivo
.bazelrcen la raíz del directorio del espacio de trabajo con 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 función experimental de línea de comandos --config={config_name} está asociada con esa marca en particular. Consulta
la documentación de las marcas usadas:
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 CPUs,
por lo que se diferencia con la marca --cpu=k8.
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 modo que estas herramientas se compilen con esa cadena de herramientas 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 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-worldDebido a que especificaste
--crosstool_top=//toolchain:clang_suiteen el archivo.bazelrc, Bazel muestra el siguiente error:No such package `toolchain`: BUILD file not found on package path.En el directorio del espacio de trabajo, crea el directorio
toolchainpara el paquete y un archivoBUILDvacío dentro del directoriotoolchain.Vuelve a ejecutar la compilación. Debido a que el paquete
toolchainaú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/BUILDEn el archivo
toolchain/BUILD, define un filegroup 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_topapunta a una regla que no proporciona el proveedorToolchainInfonecesario. Por lo tanto, debes apuntar--crosstool_topa una regla que proporcioneToolchainInfo, es decir, la reglacc_toolchain_suite. En el archivotoolchain/BUILD, reemplaza el filegroup vacío por lo siguiente:cc_toolchain_suite( name = "clang_suite", toolchains = { "k8": ":k8_toolchain", }, )El atributo
toolchainsasigna automáticamente los valores--cpu(y también--compilercuando se especifica) acc_toolchain. Aún no definiste ningún destinocc_toolchain, y Bazel se quejará de eso en breve.Vuelve a ejecutar la compilación. Bazel muestra el siguiente error:
Rule '//toolchain:k8_toolchain' does not existAhora debes definir destinos
cc_toolchainpara 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 existA 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'CcToolchainConfigInfoes un proveedor que usas para configurar tus cadenas de herramientas de C++. Para corregir este error, crea una regla de Starlark que proporcioneCcToolchainConfigInfoa Bazel creando un archivotoolchain/cc_toolchain_config.bzlcon 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 sentencia load atoolchain/BUILDjusto debajo de la sentencia package:load(":cc_toolchain_config.bzl", "cc_toolchain_config")Y reemplaza el filegroup "k8_toolchain_config" por 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 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/clangy/usr/bin/ldsean las rutas 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. Hay varias formas de resolver este problema, como usar el atributo
includesdecc_binary, pero aquí se resuelve a nivel de la cadena de herramientas con elcxx_builtin_include_directoriesparámetro 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.bzlpara 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. 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'El motivo es que el vinculador no tiene la biblioteca estándar de C++ y no puede encontrar sus símbolos. Hay muchas formas de resolver este problema, como usar el atributo
linkoptsdecc_binary. Aquí se resuelve asegurándose de que ningún destino que use la cadena de herramientas 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 este ejemplo simple.
Los puntos clave son los 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 en particular con el archivo .bazelrc
- El cc_toolchain_suite puede enumerar cc_toolchains para diferentes CPUs y
compiladores. Puedes usar marcas de línea de comandos como --cpu para diferenciar.
- Debes informarle a la cadena de herramientas 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, puedes leer sobre
los espacios de trabajo aquí. Tus herramientas podrían provenir de un
espacio de trabajo diferente, y tendrías que poner sus archivos a disposición
de cc_toolchain con dependencias de destino en atributos, como
compiler_files. También se deberían cambiar las tool_paths.
- Puedes crear funciones para personalizar qué marcas se deben pasar a
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++