Practical Bazel: Using Clang 12 C++ Toolchain on Ubuntu 20.04
Practical Bazel bazel c++ clang
Published: 2023-03-08
Practical Bazel: Using Clang 12 C++ Toolchain on Ubuntu 20.04

This blog post explains how to use a custom C++ toolchain based on clang 12 with libc++-12 in Bazel on Ubuntu 20.04.

There are many reasons why one many not want to use the default C++ compiler in Bazel:

  • The default compiler shipped with your distribution might be old and you want to use a new one
  • You want to ensure that a specific version of a compiler is used
  • You want to use a different C++ standard library (e.g. libc++ instead of libstdc++)

The instructions at Bazel Tutorial: Configure C++ Toolchains are an excellent starting point. Here’s how I got them working for clang 12 with libc++-12 on Ubuntu 20.04:

  1. Install the necessary packages with sudo apt install clang-12 libc++-12-dev libc++abi-12-dev
  2. Create the file //toolchain:clang12_cc_toolchain_config.bzl with the following contents:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
# //toolchain:clang12_cc_toolchain_config.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",
)

def _impl(ctx):
    tool_paths = [
        tool_path(
            name = "gcc",
            path = "/usr/bin/clang-12",
        ),
        tool_path(
            name = "ld",
            path = "/usr/bin/ld",
        ),
        tool_path(
            name = "ar",
            path = "/usr/bin/ar",
        ),
        tool_path(
            name = "cpp",
            path = "/usr/bin/clang-cpp-12",
        ),
        tool_path(
            name = "gcov",
            path = "/usr/bin/gcov",
        ),
        tool_path(
            name = "nm",
            path = "/usr/bin/nm",
        ),
        tool_path(
            name = "objdump",
            path = "/usr/bin/objdump",
        ),
        tool_path(
            name = "strip",
            path = "/usr/bin/strip",
        ),
    ]

    features = [
        feature(
            # We prefer libc++ to libstdc++
            name = "use_libc++_as_stdlib",
            enabled = True,
            flag_sets = [
                flag_set(
                    actions = [
                        ACTION_NAMES.cpp_compile,
                    ],
                    flag_groups = ([
                        flag_group(
                            flags = [
                                "-stdlib=libc++",
                            ],
                        ),
                    ]),
                ),
                flag_set(
                    actions = [
                        ACTION_NAMES.cpp_link_executable,
                        ACTION_NAMES.cpp_link_dynamic_library,
                        ACTION_NAMES.cpp_link_nodeps_dynamic_library,
                    ],
                    flag_groups = ([
                        flag_group(
                            flags = [
                                "-lc++",
                            ],
                        ),
                    ]),
                ),
            ],
        ),
    ]

    return cc_common.create_cc_toolchain_config_info(
        ctx = ctx,
        features = features,
        cxx_builtin_include_directories = [
            "/usr/lib/llvm-12/include",
            # On some machines the include files are in 12.0.0; others 12.0.1...
            # we include both to be safe
            "/usr/lib/llvm-12/lib/clang/12.0.0/include",
            "/usr/lib/llvm-12/lib/clang/12.0.1/include",
            "/usr/include",
            "/usr/local/include",
        ],
        toolchain_identifier = "clang12-toolchain",
        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,
    )

clang12_cc_toolchain_config = rule(
    implementation = _impl,
    attrs = {},
    provides = [CcToolchainConfigInfo],
)
  1. Create a //toolchain:BUILD.bazel with the following contents:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# //toolchain:BUILD.bazel
load(":clang12_cc_toolchain_config.bzl", "clang12_cc_toolchain_config")

filegroup(name = "empty")

clang12_cc_toolchain_config(name = "clang12_toolchain_config")

cc_toolchain(
    name = "clang12_k8_toolchain",
    all_files = ":empty",
    compiler_files = ":empty",
    dwp_files = ":empty",
    linker_files = ":empty",
    objcopy_files = ":empty",
    strip_files = ":empty",
    supports_param_files = 0,
    toolchain_config = ":clang12_toolchain_config",
    toolchain_identifier = "clang12-toolchain",
)

cc_toolchain_suite(
    name = "clang12_suite",
    toolchains = {
        "k8": ":clang12_k8_toolchain",
    },
)
  1. Modify //.bazelrc to use our clang 12 toolchain:
1
2
3
# ~/.bazelrc
# Build C++ binaries using clang-12 toolchain
build --crosstool_top=//toolchain:clang12_suite