网络知识 娱乐 C++20 以 Bazel & Clang 开始

C++20 以 Bazel & Clang 开始

C++20 如何以 Bazel & Clang 进行构建呢?

本文将介绍:

  • Bazel[1] 构建系统的安装
  • LLVM[2] 编译系统的安装
  • Clang[3] is an "LLVM native" C/C++/Objective-C compiler
  • Bazel Clang 工具链的配置
  • C++20 库与应用的构建

本文示例可见: https://github.com/ikuokuo/start-cpp20

本文是于 Ubuntu 20 上进行的实践,Windows 可以用 WSL 准备环境。

安装 Bazel,以二进制方式

Bazelisk[4] 是安装 Bazel 的推荐方式,我们安装它的二进制发布[5]即可:

cd ~nwget https://github.com/bazelbuild/bazelisk/releases/download/v1.12.0/bazelisk-linux-amd64 -O bazelisk-1.12.0-linux-amd64nchmod a+x bazelisk-*nnsudo ln -s $(pwd)/bazelisk-1.12.0-linux-amd64 /usr/local/bin/bazelnntouch WORKSPACEn# 国内下载 Bazel 可能遇到如下问题,配置 .bazeliskrc 解决n# could not resolve the version 'latest' to an actual version numbern# https://github.com/bazelbuild/bazelisk/issues/220ncat <<-EOF > .bazeliskrcnBAZELISK_BASE_URL=https://github.com/bazelbuild/bazel/releases/downloadnUSE_BAZEL_VERSION=5.2.0nEOFnnbazel versionn

更多方式,可见官方文档[6]。进一步,推荐安装 buildtools[7],下载后软链一下:

sudo ln -s $(pwd)/buildifier-5.1.0-linux-amd64 /usr/local/bin/buildifiernsudo ln -s $(pwd)/buildozer-5.1.0-linux-amd64 /usr/local/bin/buildozern

Bazel 如何构建 C++ 项目,可见我的 Start Bazel[8] 笔记。

安装 LLVM,以源码方式

Clang 有关 std::fromat 文本格式化的特性,默认未开启:

The paper is implemented but still marked as an incomplete feature (the feature-test macro is not set and the libary is only available when built with LIBCXX_ENABLE_INCOMPLETE_FEATURES). Not yet implemented LWG-issues will cause API and ABI breakage.

C++20 特性,编译器支持情况:

  • C++ compiler support[9]
  • libc++ C++20 Status[10]

因此,这里以源码方式安装 LLVM,需要构建 Clang & libc++:

  • Building Clang[11]
  • Building libc++[12]

git clone -b llvmorg-14.0.6 --depth 1 https://github.com/llvm/llvm-project.gitnncd llvm-projectnmkdir _buildncd _buildnn# llvm install path, such as /usr/local/llvmnLLVM_PREFIX=$HOME/Apps/llvm-14.0.6nncmake -DCMAKE_BUILD_TYPE=Release n-DCMAKE_INSTALL_PREFIX=$LLVM_PREFIX n-DLLVM_ENABLE_PROJECTS=clang n-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" n-DLIBCXX_ENABLE_INCOMPLETE_FEATURES=ON n../llvmnnmake -j`nproc`nmake installnnsudo ln -s $LLVM_PREFIX /usr/local/llvmnncat <<-EOF >> ~/.bashrcn# llvmnexport LLVM_HOME=/usr/local/llvmnexport PATH=$LLVM_HOME/bin:$PATHnexport LD_LIBRARY_PATH=$LLVM_HOME/lib/x86_64-unknown-linux-gnu:$LD_LIBRARY_PATHnEOFnnllvm-config --versionnclang --versionn

LLVM_PREFIX 安装路径自己决定。最后,编译测试:

cat <<-EOF > hello.ccn#include <format>n#include <iostream>nnint main() {n std::string message = std::format("The answer is {}.", 42);n std::cout << message << std::endl;n}nEOFnnclang++ -std=c++20 -stdlib=libc++ hello.cc -o hellonn./hellon

安装 LLVM,以二进制方式

可省略该节。本文实践未用此方式,因为想开启更多 C++20 特性。这里仅作记录,有需要可参考。

方式 1. 安装二进制发布[13]

cd ~nwget https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xzntar -xf clang+llvm-*.tar.xznnsudo ln -s $(pwd)/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04 /usr/local/llvmnncat <<-EOF >> ~/.bashrcn# llvmnexport LLVM_HOME=/usr/local/llvmnexport PATH=$LLVM_HOME/bin:$PATHnEOFnnllvm-config --versionnclang --versionn

方式 2. 用 apt 进行安装: https://apt.llvm.org/

方式 3. 用已配好的工具链: LLVM toolchain for Bazel[14]

配置 Clang 工具链

本文依照 Bazel Tutorial: Configure C++ Toolchains[15] 步骤配置的 Clang 工具链,最后项目根目录会多如下文件:

  • WORKSPACE[16]
  • .bazelrc[17]
  • toolchain/BUILD[18]
  • toolchain/cc_toolchain_config.bzl[19]

WORKSPACE 表示 Bazel 工作区,内容空。

.bazelrc 允许 --config=clang_config 启用 Clang 工具链:

# Use our custom-configured c++ toolchain.nbuild:clang_config --crosstool_top=//toolchain:clang_suitenn# Use --cpu as a differentiator.nbuild:clang_config --cpu=linux_x86_64nn# Use the default Bazel C++ toolchain to build the tools used during the build.nbuild:clang_config --host_crosstool_top=@bazel_tools//tools/cpp:toolchainn

toolchain/BUILD 配置 Clang 工具链信息:

load(":cc_toolchain_config.bzl", "cc_toolchain_config")nnpackage(default_visibility = ["//visibility:public"])nn#filegroup(name = "clang_suite")nncc_toolchain_suite(n name = "clang_suite",n toolchains = {n "linux_x86_64": ":linux_x86_64_toolchain",n },n)nnfilegroup(name = "empty")nncc_toolchain(n name = "linux_x86_64_toolchain",n toolchain_identifier = "linux_x86_64-toolchain",n toolchain_config = ":linux_x86_64_toolchain_config",n all_files = ":empty",n compiler_files = ":empty",n dwp_files = ":empty",n linker_files = ":empty",n objcopy_files = ":empty",n strip_files = ":empty",n supports_param_files = 0,n)nn#filegroup(name = "linux_x86_64_toolchain_config")nncc_toolchain_config(name = "linux_x86_64_toolchain_config")n

toolchain/cc_toolchain_config.bzl 配置 Clang 工具链规则:

# C++ Toolchain Configurationn# https://bazel.build/docs/cc-toolchain-config-referencen# https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzlnload("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")nload(n "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",n "feature",n "flag_group",n "flag_set",n "tool_path",n)nnall_compile_actions = [n ACTION_NAMES.c_compile,n ACTION_NAMES.cpp_compile,n ACTION_NAMES.linkstamp_compile,n ACTION_NAMES.assemble,n ACTION_NAMES.preprocess_assemble,n ACTION_NAMES.cpp_header_parsing,n ACTION_NAMES.cpp_module_compile,n ACTION_NAMES.cpp_module_codegen,n]nnall_link_actions = [n ACTION_NAMES.cpp_link_executable,n ACTION_NAMES.cpp_link_dynamic_library,n ACTION_NAMES.cpp_link_nodeps_dynamic_library,n]nndef _impl(ctx):n llvm_version = "14.0.6"n llvm_prefix = "/home/john/Apps/llvm-{}".format(llvm_version)n llvm_bindir = llvm_prefix + "/bin"nn tool_paths = [n tool_path(n name = "gcc",n path = llvm_bindir + "/clang",n ),n tool_path(n name = "ld",n path = llvm_bindir + "/ld.lld",n ),n tool_path(n name = "ar",n path = llvm_bindir + "/llvm-ar",n ),n tool_path(n name = "cpp",n path = llvm_bindir + "/clang-cpp",n ),n tool_path(n name = "gcov",n path = llvm_bindir + "/llvm-cov",n ),n tool_path(n name = "nm",n path = llvm_bindir + "/llvm-nm",n ),n tool_path(n name = "objdump",n path = llvm_bindir + "/llvm-objdump",n ),n tool_path(n name = "strip",n path = llvm_bindir + "/llvm-strip",n ),n ]nn features = [n feature(n name = "default_compiler_flags",n enabled = True,n flag_sets = [n flag_set(n actions = all_compile_actions,n flag_groups = ([n flag_group(n flags = [n "-O2", "-DNDEBUG",n "-Wall", "-Wextra", "-Wpedantic", "-fPIC",n "-std=c++20", "-stdlib=libc++",n ],n ),n ]),n ),n ],n ),n feature(n name = "default_linker_flags",n enabled = True,n flag_sets = [n flag_set(n actions = all_link_actions,n flag_groups = ([n flag_group(n flags = [n "-lc++", "-lc++abi",n "-lm", "-ldl", "-lpthread",n ],n ),n ]),n ),n ],n ),n ]nn return cc_common.create_cc_toolchain_config_info(n ctx = ctx,n features = features,n cxx_builtin_include_directories = [n llvm_prefix + "/lib/clang/{}/include".format(llvm_version),n llvm_prefix + "/include/x86_64-unknown-linux-gnu/c++/v1",n llvm_prefix + "/include/c++/v1",n "/usr/local/include",n "/usr/include/x86_64-linux-gnu",n "/usr/include",n ],n toolchain_identifier = "local",n host_system_name = "local",n target_system_name = "local",n target_cpu = "linux_x86_64",n target_libc = "unknown",n compiler = "clang",n abi_version = "unknown",n abi_libc_version = "unknown",n tool_paths = tool_paths,n )nncc_toolchain_config = rule(n implementation = _impl,n attrs = {},n provides = [CcToolchainConfigInfo],n)n

llvm_prefix 给到自己的 LLVM 安装路径。

构建 C++20 库与应用

本文示例的 code/00/[20] 路径下准备了 C++20 的库与应用:

code/00/n├── BUILDn├── greetn│ ├── BUILDn│ ├── greet.ccn│ └── greet.hn└── main.ccn

编写 binary

main.cc:

#include <format>n#include <iostream>n#include <string>n#include <string_view>nn#include "greet/greet.h"nntemplate <typename... Args>nstd::string dyna_print(std::string_view rt_fmt_str, Args&&... args) {n return std::vformat(rt_fmt_str, std::make_format_args(args...));n}nnint main() {n std::cout << greet::hello("world") << std::endl;nn std::string fmt;n for (int i{}; i != 3; ++i) {n fmt += "{} "; // constructs the formatting stringn std::cout << fmt << " : ";n std::cout << dyna_print(fmt, "alpha", 'Z', 3.14, "unused");n std::cout << 'n';n }n}n

BUILD:

load("@rules_cc//cc:defs.bzl", "cc_binary")nncc_binary(n name = "main",n srcs = ["main.cc"],n deps = [n "//code/00/greet:greet",n ],n)n

编写 library

greet.h:

#pragma oncenn#include <string>n#include <string_view>nnnamespace greet {nnstd::string hello(std::string_view who);nn} // namespace greetn

greet.cc:

#include "greet.h"nn#include <format>n#include <utility>nnnamespace greet {nnstd::string hello(std::string_view who) {n return std::format("Hello {}!", std::move(who));n}nn} // namespace greetn

BUILD:

load("@rules_cc//cc:defs.bzl", "cc_library")nnpackage(default_visibility = ["//visibility:public"])nncc_library(n name = "greet",n srcs = [n "greet.cc",n ],n hdrs = [n "greet.h",n ],n)n

Bazel 构建

bazel build --config=clang_config //code/00:mainn

运行测试

$ bazel-bin/code/00/mainnHello world!n{} : alphan{} {} : alpha Zn{} {} {} : alpha Z 3.14n

查看依赖

sudo apt update && sudo apt install graphviz xdot -yn# viewnxdot <(bazel query --notool_deps --noimplicit_deps "deps(//code/00:main)" --output graph)n# to svgndot -Tsvg <(bazel query --notool_deps --noimplicit_deps "deps(//code/00:main)" --output graph) -o 00_main.svgnC++20 以 Bazel & Clang 开始

参考

  • Bazel Tutorial
  • Configure C++ Toolchains[21]
  • Build a C++ Project[22]
  • Bazel Issue
  • Support C++20 modules #4005[23]
  • Project Example
  • How to Use C++20 Modules with Bazel and Clang[24]
  • bazel-cpp20: Template for bazel with C++20[25]
  • Clang toolchain[26]

GoCoding 个人实践的经验分享,可关注公众号!

脚注

[1] Bazel: https://bazel.build/

[2] LLVM: https://llvm.org/

[3] Clang: https://clang.llvm.org/

[4] Bazelisk: https://github.com/bazelbuild/bazelisk

[5] 二进制发布: https://github.com/bazelbuild/bazelisk/releases

[6] 官方文档: https://bazel.build/install

[7] buildtools: https://github.com/bazelbuild/buildtools/releases

[8] Start Bazel: https://github.com/ikuokuo/start-cpp20/blob/main/tutorials/start-bazel/README.md

[9] C++ compiler support: https://en.cppreference.com/w/cpp/compiler_support

[10] libc++ C++20 Status: https://libcxx.llvm.org/Status/Cxx20.html

[11] Building Clang: https://clang.llvm.org/get_started.html

[12] Building libc++: https://libcxx.llvm.org/BuildingLibcxx.html

[13] 二进制发布: https://github.com/llvm/llvm-project/releases

[14] LLVM toolchain for Bazel: https://github.com/grailbio/bazel-toolchain

[15] Bazel Tutorial: Configure C++ Toolchains: https://bazel.build/tutorials/cc-toolchain-config

[16] WORKSPACE: https://github.com/ikuokuo/start-cpp20/blob/main/WORKSPACE

[17] .bazelrc: https://github.com/ikuokuo/start-cpp20/blob/main/.bazelrc

[18] toolchain/BUILD: https://github.com/ikuokuo/start-cpp20/blob/main/toolchain/BUILD

[19] toolchain/cc_toolchain_config.bzl: https://github.com/ikuokuo/start-cpp20/blob/main/toolchain/cc_toolchain_config.bzl

[20] code/00/: https://github.com/ikuokuo/start-cpp20/tree/main/code/00

[21] Configure C++ Toolchains: https://bazel.build/tutorials/cc-toolchain-config

[22] Build a C++ Project: https://bazel.build/tutorials/cpp

[23] Support C++20 modules #4005: https://github.com/bazelbuild/bazel/issues/4005

[24] How to Use C++20 Modules with Bazel and Clang: https://buildingblock.ai/cpp20-modules-bazel

[25] bazel-cpp20: Template for bazel with C++20: https://github.com/jwmcglynn/bazel-cpp20

[26] Clang toolchain: https://github.com/hlopko/clang_toolchain