Instructivo de Bazel: Configura cadenas de herramientas de C++

Informar un problema . Ver fuente . Por la noche · 7.2 · 7.1 · 7.0 · 6.5 · 6.4

En este instructivo, se usa una situación de ejemplo para describir cómo configurar C++ cadenas de herramientas 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 configuración para cc_toolchain de modo que Bazel pueda compilar la aplicación. con clang
  • Compila el objeto binario de C++ ejecutando bazel build //main:hello-world en una máquina Linux
  • 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 usas Linux y que compilaste correctamente C++ y, además, instalaron las herramientas y bibliotecas correspondientes. 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:

  1. Si aún no lo has hecho, descarga e instala Bazel 7.0.2 o una versión posterior.

  2. Agrega un archivo WORKSPACE vacío en la carpeta raíz.

  3. Agrega el siguiente destino cc_binary al archivo main/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 como process-wrapper, se especifica la cadena de herramientas predeterminada de C++ preexistente para la plataforma host. Esto permite que estas herramientas internas compilen de la que se creó en este instructivo. Por lo tanto, el objetivo cc_binary también se compila con la cadena de herramientas predeterminada.

  4. 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 con más detalle el funcionamiento interno, 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 usando @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 de forma repetida y elimina cada error uno por uno, como se describe a continuación.

También se supone que es clang version 9.0.1, aunque los detalles solo deben cambiar entre las diferentes versiones de Clang.

  1. Agrega toolchain/BUILD con

    filegroup(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

    register_toolchains(
        "//toolchain:cc_toolchain_for_linux_x86_64"
    )
    

    En este paso, se define un cc_toolchain y se lo vincula a un destino toolchain para la configuración del host.

  2. Vuelve a ejecutar la compilación. Debido a que el paquete toolchain aún no define el linux_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.
    
  3. 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")
    
  4. 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 se usa para configurar de tus cadenas de herramientas de C++. Para corregir este error, crea una regla de Starlark que proporciona CcToolchainConfigInfo a Bazel haciendo un toolchain/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 regla cc_toolchain_config, agrega una carga a toolchain/BUILD justo debajo de la declaración del paquete:

    load(":cc_toolchain_config.bzl", "cc_toolchain_config")
    

    Y reemplacen "linux_x86_64_toolchain_config" grupo de archivos con una declaración de una regla cc_toolchain_config:

    cc_toolchain_config(name = "linux_x86_64_toolchain_config")
    
  5. 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.

  6. 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. Existen varias formas de resolver esto, como usar el atributo includes de cc_binary, pero aquí esto se resuelve a nivel de la cadena de herramientas con el cxx_builtin_include_directories parámetro de cc_common.create_cc_toolchain_config_info. Ten en cuenta que si estás usando una versión diferente de clang, 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-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,
    )
    
  7. 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 de cc_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 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],
    )
    
  8. Cuando ejecutes bazel build //main:hello-world, debería compilar el objeto binario de forma correcta para el host.

  9. En toolchain/BUILD, copia cc_toolchain_config, cc_toolchain y toolchain se orienta y reemplaza linux_x86_64 por android_x86_64 en el objetivo. y nombres de usuario.

    En WORKSPACE, registra la cadena de herramientas para Android.

    register_toolchains(
        "//toolchain:cc_toolchain_for_linux_x86_64",
        "//toolchain:cc_toolchain_for_android_x86_64"
    )
    
  10. 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 deberían tener diferentes configuraciones del conjunto de herramientas de C++. Tú puedes modificar el cc_toolchain_config existente para ver las diferencias crea reglas independientes (es decir, el proveedor CcToolchainConfigInfo) para diferentes y plataformas de Google Cloud.

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 platforms coincidente en la línea de comandos para que Bazel haga lo siguiente: resuelve a la cadena de herramientas para 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 se accede a las herramientas del sistema. Si estás interesado en un enfoque más autónomo, puedes leer acerca de lugares de trabajo. Tus herramientas podrían provenir de un 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++