前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++20 以 Bazel & Clang 开始

C++20 以 Bazel & Clang 开始

作者头像
GoCoding
发布2022-11-07 17:54:04
1.3K0
发布2022-11-07 17:54:04
举报
文章被收录于专栏:GoCodingGoCoding

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]即可:

代码语言:javascript
复制
cd ~
wget https://github.com/bazelbuild/bazelisk/releases/download/v1.12.0/bazelisk-linux-amd64 -O bazelisk-1.12.0-linux-amd64
chmod a+x bazelisk-*

sudo ln -s $(pwd)/bazelisk-1.12.0-linux-amd64 /usr/local/bin/bazel

touch WORKSPACE
# 国内下载 Bazel 可能遇到如下问题,配置 .bazeliskrc 解决
# could not resolve the version 'latest' to an actual version number
#  https://github.com/bazelbuild/bazelisk/issues/220
cat <<-EOF > .bazeliskrc
BAZELISK_BASE_URL=https://github.com/bazelbuild/bazel/releases/download
USE_BAZEL_VERSION=5.2.0
EOF

bazel version

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

代码语言:javascript
复制
sudo ln -s $(pwd)/buildifier-5.1.0-linux-amd64 /usr/local/bin/buildifier
sudo ln -s $(pwd)/buildozer-5.1.0-linux-amd64 /usr/local/bin/buildozer

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]
代码语言:javascript
复制
git clone -b llvmorg-14.0.6 --depth 1 https://github.com/llvm/llvm-project.git

cd llvm-project
mkdir _build
cd _build

# llvm install path, such as /usr/local/llvm
LLVM_PREFIX=$HOME/Apps/llvm-14.0.6

cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$LLVM_PREFIX \
-DLLVM_ENABLE_PROJECTS=clang \
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \
-DLIBCXX_ENABLE_INCOMPLETE_FEATURES=ON \
../llvm

make -j`nproc`
make install

sudo ln -s $LLVM_PREFIX /usr/local/llvm

cat <<-EOF >> ~/.bashrc
# llvm
export LLVM_HOME=/usr/local/llvm
export PATH=\$LLVM_HOME/bin:\$PATH
export LD_LIBRARY_PATH=\$LLVM_HOME/lib/x86_64-unknown-linux-gnu:\$LD_LIBRARY_PATH
EOF

llvm-config --version
clang --version

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

代码语言:javascript
复制
cat <<-EOF > hello.cc
#include <format>
#include <iostream>

int main() {
  std::string message = std::format("The answer is {}.", 42);
  std::cout << message << std::endl;
}
EOF

clang++ -std=c++20 -stdlib=libc++ hello.cc -o hello

./hello

安装 LLVM,以二进制方式

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

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

代码语言:javascript
复制
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.xz
tar -xf clang+llvm-*.tar.xz

sudo ln -s $(pwd)/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04 /usr/local/llvm

cat <<-EOF >> ~/.bashrc
# llvm
export LLVM_HOME=/usr/local/llvm
export PATH=\$LLVM_HOME/bin:\$PATH
EOF

llvm-config --version
clang --version

方式 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 工具链:

代码语言:javascript
复制
# 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 工具链信息:

代码语言:javascript
复制
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 工具链规则:

代码语言:javascript
复制
# C++ Toolchain Configuration
#  https://bazel.build/docs/cc-toolchain-config-reference
#  https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl
load("@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/[20] 路径下准备了 C++20 的库与应用:

代码语言:javascript
复制
code/00/
├── BUILD
├── greet
│   ├── BUILD
│   ├── greet.cc
│   └── greet.h
└── main.cc

编写 binary

main.cc:

代码语言:javascript
复制
#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:

代码语言:javascript
复制
load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
    name = "main",
    srcs = ["main.cc"],
    deps = [
        "//code/00/greet:greet",
    ],
)

编写 library

greet.h:

代码语言:javascript
复制
#pragma once

#include <string>
#include <string_view>

namespace greet {

std::string hello(std::string_view who);

}  // namespace greet

greet.cc:

代码语言:javascript
复制
#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:

代码语言:javascript
复制
load("@rules_cc//cc:defs.bzl", "cc_library")

package(default_visibility = ["//visibility:public"])

cc_library(
    name = "greet",
    srcs = [
        "greet.cc",
    ],
    hdrs = [
        "greet.h",
    ],
)

Bazel 构建

代码语言:javascript
复制
bazel build --config=clang_config //code/00:main

运行测试

代码语言:javascript
复制
$ bazel-bin/code/00/main
Hello world!
{}  : alpha
{} {}  : alpha Z
{} {} {}  : alpha Z 3.14

查看依赖

代码语言:javascript
复制
sudo apt update && sudo apt install graphviz xdot -y
# view
xdot <(bazel query --notool_deps --noimplicit_deps "deps(//code/00:main)" --output graph)
# to svg
dot -Tsvg <(bazel query --notool_deps --noimplicit_deps "deps(//code/00:main)" --output graph) -o 00_main.svg

参考

  • 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]

脚注

[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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-07-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GoCoding 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安装 Bazel,以二进制方式
  • 安装 LLVM,以源码方式
  • 安装 LLVM,以二进制方式
  • 配置 Clang 工具链
  • 构建 C++20 库与应用
    • 编写 binary
      • 编写 library
        • Bazel 构建
          • 运行测试
            • 查看依赖
            • 参考
              • 脚注
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档