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

本文将介绍:

  • Bazel 构建零碎的装置
  • LLVM 编译系统的装置

    • Clang is an "LLVM native" C/C++/Objective-C compiler
  • Bazel Clang 工具链的配置
  • C++20 库与利用的构建

本文示例可见: https://github.com/ikuokuo/st...

本文是于 Ubuntu 20 上进行的实际,Windows 能够用 WSL 筹备环境。

装置 Bazel,以二进制形式

Bazelisk 是装置 Bazel 的举荐形式,咱们装置它的二进制公布即可:

cd ~wget https://github.com/bazelbuild/bazelisk/releases/download/v1.12.0/bazelisk-linux-amd64 -O bazelisk-1.12.0-linux-amd64chmod a+x bazelisk-*sudo ln -s $(pwd)/bazelisk-1.12.0-linux-amd64 /usr/local/bin/bazeltouch WORKSPACE# 国内下载 Bazel 可能遇到如下问题,配置 .bazeliskrc 解决# could not resolve the version 'latest' to an actual version number#  https://github.com/bazelbuild/bazelisk/issues/220cat <<-EOF > .bazeliskrcBAZELISK_BASE_URL=https://github.com/bazelbuild/bazel/releases/downloadUSE_BAZEL_VERSION=5.2.0EOFbazel version

更多形式,可见官网文档。进一步,举荐装置 buildtools,下载后软链一下:

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

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

装置 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
  • libc++ C++20 Status

因而,这里以源码形式装置 LLVM,须要构建 Clang & libc++:

  • Building Clang
  • Building libc++
git clone -b llvmorg-14.0.6 --depth 1 https://github.com/llvm/llvm-project.gitcd llvm-projectmkdir _buildcd _build# llvm install path, such as /usr/local/llvmLLVM_PREFIX=$HOME/Apps/llvm-14.0.6cmake -DCMAKE_BUILD_TYPE=Release \-DCMAKE_INSTALL_PREFIX=$LLVM_PREFIX \-DLLVM_ENABLE_PROJECTS=clang \-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \-DLIBCXX_ENABLE_INCOMPLETE_FEATURES=ON \../llvmmake -j`nproc`make installsudo ln -s $LLVM_PREFIX /usr/local/llvmcat <<-EOF >> ~/.bashrc# llvmexport LLVM_HOME=/usr/local/llvmexport PATH=\$LLVM_HOME/bin:\$PATHexport LD_LIBRARY_PATH=\$LLVM_HOME/lib/x86_64-unknown-linux-gnu:\$LD_LIBRARY_PATHEOFllvm-config --versionclang --version

LLVM_PREFIX 装置门路本人决定。最初,编译测试:

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

装置 LLVM,以二进制形式

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

形式 1. 装置二进制公布:

cd ~wget 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.xztar -xf clang+llvm-*.tar.xzsudo ln -s $(pwd)/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04 /usr/local/llvmcat <<-EOF >> ~/.bashrc# llvmexport LLVM_HOME=/usr/local/llvmexport PATH=\$LLVM_HOME/bin:\$PATHEOFllvm-config --versionclang --version

形式 2. 用 apt 进行装置: https://apt.llvm.org/

形式 3. 用已配好的工具链: LLVM toolchain for Bazel

配置 Clang 工具链

本文按照 Bazel Tutorial: Configure C++ Toolchains 步骤配置的 Clang 工具链,最初我的项目根目录会多如下文件:

  • WORKSPACE
  • .bazelrc
  • toolchain/BUILD
  • toolchain/cc_toolchain_config.bzl

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

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

# Use our custom-configured c++ toolchain.build:clang_config --crosstool_top=//toolchain:clang_suite# Use --cpu as a differentiator.build:clang_config --cpu=linux_x86_64# 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

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

load(":cc_toolchain_config.bzl", "cc_toolchain_config")package(default_visibility = ["//visibility:public"])#filegroup(name = "clang_suite")cc_toolchain_suite(    name = "clang_suite",    toolchains = {        "linux_x86_64": ":linux_x86_64_toolchain",    },)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,)#filegroup(name = "linux_x86_64_toolchain_config")cc_toolchain_config(name = "linux_x86_64_toolchain_config")

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

# C++ Toolchain Configuration#  https://bazel.build/docs/cc-toolchain-config-reference#  https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzlload("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")load(    "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",    "feature",    "flag_group",    "flag_set",    "tool_path",)all_compile_actions = [    ACTION_NAMES.c_compile,    ACTION_NAMES.cpp_compile,    ACTION_NAMES.linkstamp_compile,    ACTION_NAMES.assemble,    ACTION_NAMES.preprocess_assemble,    ACTION_NAMES.cpp_header_parsing,    ACTION_NAMES.cpp_module_compile,    ACTION_NAMES.cpp_module_codegen,]all_link_actions = [    ACTION_NAMES.cpp_link_executable,    ACTION_NAMES.cpp_link_dynamic_library,    ACTION_NAMES.cpp_link_nodeps_dynamic_library,]def _impl(ctx):    llvm_version = "14.0.6"    llvm_prefix = "/home/john/Apps/llvm-{}".format(llvm_version)    llvm_bindir = llvm_prefix + "/bin"    tool_paths = [        tool_path(            name = "gcc",            path = llvm_bindir + "/clang",        ),        tool_path(            name = "ld",            path = llvm_bindir + "/ld.lld",        ),        tool_path(            name = "ar",            path = llvm_bindir + "/llvm-ar",        ),        tool_path(            name = "cpp",            path = llvm_bindir + "/clang-cpp",        ),        tool_path(            name = "gcov",            path = llvm_bindir + "/llvm-cov",        ),        tool_path(            name = "nm",            path = llvm_bindir + "/llvm-nm",        ),        tool_path(            name = "objdump",            path = llvm_bindir + "/llvm-objdump",        ),        tool_path(            name = "strip",            path = llvm_bindir + "/llvm-strip",        ),    ]    features = [        feature(            name = "default_compiler_flags",            enabled = True,            flag_sets = [                flag_set(                    actions = all_compile_actions,                    flag_groups = ([                        flag_group(                            flags = [                                "-O2", "-DNDEBUG",                                "-Wall", "-Wextra", "-Wpedantic", "-fPIC",                                "-std=c++20", "-stdlib=libc++",                            ],                        ),                    ]),                ),            ],        ),        feature(            name = "default_linker_flags",            enabled = True,            flag_sets = [                flag_set(                    actions = all_link_actions,                    flag_groups = ([                        flag_group(                            flags = [                                "-lc++", "-lc++abi",                                "-lm", "-ldl", "-lpthread",                            ],                        ),                    ]),                ),            ],        ),    ]    return cc_common.create_cc_toolchain_config_info(        ctx = ctx,        features = features,        cxx_builtin_include_directories = [            llvm_prefix + "/lib/clang/{}/include".format(llvm_version),            llvm_prefix + "/include/x86_64-unknown-linux-gnu/c++/v1",            llvm_prefix + "/include/c++/v1",            "/usr/local/include",            "/usr/include/x86_64-linux-gnu",            "/usr/include",        ],        toolchain_identifier = "local",        host_system_name = "local",        target_system_name = "local",        target_cpu = "linux_x86_64",        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],)

llvm_prefix 给到本人的 LLVM 装置门路。

构建 C++20 库与利用

本文示例的 code/00/ 门路下筹备了 C++20 的库与利用:

code/00/├── BUILD├── greet│   ├── BUILD│   ├── greet.cc│   └── greet.h└── main.cc

编写 binary

main.cc:

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

BUILD:

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

编写 library

greet.h:

#pragma once#include <string>#include <string_view>namespace greet {std::string hello(std::string_view who);}  // namespace greet

greet.cc:

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

BUILD:

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

Bazel 构建

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

运行测试

$ bazel-bin/code/00/mainHello world!{}  : alpha{} {}  : alpha Z{} {} {}  : alpha Z 3.14

查看依赖

sudo apt update && sudo apt install graphviz xdot -y# viewxdot <(bazel query --notool_deps --noimplicit_deps "deps(//code/00:main)" --output graph)# to svgdot -Tsvg <(bazel query --notool_deps --noimplicit_deps "deps(//code/00:main)" --output graph) -o 00_main.svg

更多参考

  • Bazel Tutorial

    • Configure C++ Toolchains
    • Build a C++ Project
  • Bazel Issue

    • Support C++20 modules #4005
  • Project Example

    • How to Use C++20 Modules with Bazel and Clang
    • bazel-cpp20: Template for bazel with C++20
    • Clang toolchain
GoCoding 集体实际的教训分享,可关注公众号!