本教程通过一个示例场景,介绍了如何为项目配置 C++ 工具链。
学习内容
在本教程中,您将学习如何:
- 设置构建环境
- 使用 --toolchain_resolution_debug调试工具链解析
- 配置 C++ 工具链
- 创建一个 Starlark 规则,为 cc_toolchain提供额外的配置,以便 Bazel 可以使用clang构建应用
- 通过在 Linux 机器上运行 bazel build //main:hello-world来构建 C++ 二进制文件
- 运行 bazel build //main:hello-world --platforms=//:android_x86_64,交叉编译 Android 的二进制文件
准备工作
本教程假定您使用的是 Linux,并且已成功构建 C++ 应用并安装了相应的工具和库。本教程使用 clang version 16,您可以在自己的系统上安装该工具。
设置构建环境
按如下方式设置构建环境:
- 在根文件夹中添加一个空的 - MODULE.bazel文件。
- 将以下 - cc_binary目标添加到- main/BUILD文件中:- cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )- 由于 Bazel 在构建期间会使用许多以 C++ 编写的内部工具(例如 - process-wrapper),因此系统会为主机平台指定预先存在的默认 C++ 工具链。这样一来,这些内部工具就可以使用本教程中创建的工具链进行构建。因此,- cc_binary目标也是使用默认工具链构建的。
- 使用以下命令运行 build: - bazel build //main:hello-world- 即使 - MODULE.bazel中未注册任何工具链,构建也会成功。- 如需进一步了解其内部运作方式,请运行以下命令: - bazel build //main:hello-world --toolchain_resolution_debug='@bazel_tools//tools/cpp:toolchain_type' INFO: ToolchainResolution: Target platform @@platforms//host:host: Selected execution platform @@platforms//host:host, type @@bazel_tools//tools/cpp:toolchain_type -> toolchain @@bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8- 如果不指定 - --platforms,Bazel 会使用- @bazel_tools+cc_configure_extension+local_config_cc//:cc-compiler-k8为- @platforms//host构建目标。
配置 C++ 工具链
如需配置 C++ 工具链,请按照以下说明重复构建应用,并逐个消除每个错误。
它还假设了 clang version 9.0.1,尽管不同版本的 clang 之间的详细信息只会略有变化。
- 添加了 - toolchain/BUILD- 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", ], )- 然后添加适当的依赖项,并使用 - MODULE.bazel注册该工具链- bazel_dep(name = "platforms", version = "0.0.10") register_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64" )- 此步骤定义了 - cc_toolchain并将其绑定到主机配置的- toolchain目标。
- 再次运行 build。由于 - toolchain软件包尚未定义- linux_x86_64_toolchain_config目标,因此 Bazel 会抛出以下错误:- 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.
- 在 - toolchain/BUILD文件中,按如下方式定义一个空文件组:- package(default_visibility = ["//visibility:public"]) filegroup(name = "linux_x86_64_toolchain_config")
- 再次运行 build。Bazel 会抛出以下错误: - '//toolchain:linux_x86_64_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'.- CcToolchainConfigInfo是一种用于配置 C++ 工具链的提供程序。如需修正此错误,请创建一个通过以下内容提供- CcToolchainConfigInfo的 Starlark 规则:- toolchain/cc_toolchain_config.bzl文件:- 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()创建所需的提供方- CcToolchainConfigInfo。如需使用- cc_toolchain_config规则,请在- toolchain/BUILD中添加加载语句,紧跟在 package 语句下方:- load(":cc_toolchain_config.bzl", "cc_toolchain_config")- 并将“linux_x86_64_toolchain_config”filegroup 替换为 - cc_toolchain_config规则的声明:- cc_toolchain_config(name = "linux_x86_64_toolchain_config")
- 再次运行 build。Bazel 会抛出以下错误: - .../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`- 此时,Bazel 已有足够的信息来尝试构建代码,但它仍然不知道要使用哪些工具来完成所需的构建操作。您将修改 Starlark 规则实现,以告知 Bazel 要使用哪些工具。为此,您需要使用 - @bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl中的- tool_path()构造函数:- # 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 )- 确保 - /usr/bin/clang和- /usr/bin/ld是您系统的正确路径。
- 再次运行 build。Bazel 会抛出以下错误: - 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 需要知道在何处搜索包含的标头。解决此问题的方法有多种,例如使用 - cc_binary的- includes属性,但在此示例中,我们通过- cc_common.create_cc_toolchain_config_info的- cxx_builtin_include_directories参数在工具链级别解决了此问题。请注意,如果您使用的是其他版本的- clang,则包含路径会有所不同。这些路径也可能会因分发版本而异。- 修改 - toolchain/cc_toolchain_config.bzl中的返回值,使其如下所示:- 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, )
- 再次运行 build 命令,您会看到类似如下的错误: - /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'- 这是因为链接器缺少 C++ 标准库,并且找不到其符号。解决此问题的方法有很多,例如使用 - cc_binary的- linkopts属性。这里通过确保使用工具链的任何目标都不必指定此标志来解决此问题。- 将以下代码复制到 - 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], )
- 运行 - bazel build //main:hello-world后,它应该最终成功为主机构建二进制文件。
- 在 - toolchain/BUILD中,复制- cc_toolchain_config、- cc_toolchain和- toolchain目标,并将目标名称中的- linux_x86_64替换为- android_x86_64。- 在 - MODULE.bazel中,注册 Android 的工具链- register_toolchains( "//toolchain:cc_toolchain_for_linux_x86_64", "//toolchain:cc_toolchain_for_android_x86_64" )
- 运行 - bazel build //main:hello-world --android_platforms=//toolchain:android_x86_64以构建 Android 的二进制文件。
实际上,Linux 和 Android 应该具有不同的 C++ 工具链配置。您可以修改现有 cc_toolchain_config 以体现差异,也可以为不同的平台创建单独的规则(即 CcToolchainConfigInfo 提供程序)。
检查您的作业
在本教程中,您学习了如何配置基本的 C++ 工具链,但工具链的功能远不止此示例所示。
关键要点如下:
- 您需要在命令行中指定匹配的 platforms标志,以便 Bazel 解析为平台上具有相同限制值的工具链。该文档包含有关特定于语言的配置标志的更多信息。
- 您必须让工具链知道工具所在的位置。在本教程中,有一个简化版本,您可以通过该版本从系统访问工具。如果您对更独立的实现方式感兴趣,可以阅读有关外部依赖项的文章。您的工具可能来自其他模块,您必须通过对属性(例如 compiler_files)设置目标依赖项,使这些工具的文件可供cc_toolchain使用。tool_paths也需要更改。
- 您可以创建功能来定制应将哪些标志传递给不同的操作(无论是关联还是任何其他类型的操作)。
深入阅读
如需了解详情,请参阅 C++ 工具链配置