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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
|
---
layout: documentation
title: Configuring C++ toolchains
---
# Configuring C++ toolchains
## Overview
This tutorial uses an example scenario to describe how to configure C++
toolchains for a project. It's based on an
[example C++ project](https://github.com/bazelbuild/examples/tree/master/cpp-tutorial/stage1)
that builds error-free using `clang`.
In this tutorial, you will create a Starlark rule that provides additional
configuration for the `cc_toolchain` so that Bazel can build the application
with `clang`. The expected outcome is to run
`bazel build --config=clang_config //main:hello-world` on a Linux machine and
build the C++ application. For additional details please visit
[C++ toolchain configuration](../cc-toolchain-config-reference.html)
## Setting up the build environment
This tutorial assumes you are on Linux on which you have successfully built
C++ applications - in other words, we assume that appropriate tooling and
libraries have been installed. The tutorial uses `clang version 9.0.1` which you
can install on your system.
Set up your build environment as follows:
1. If you have not already done so,
[download and install Bazel 0.23](../install-ubuntu.html) or later.
2. Download the
[example C++ project](https://github.com/bazelbuild/examples/tree/master/cpp-tutorial/stage1)
from GitHub and place it in an empty directory on your local machine.
3. Add the following `cc_binary` target to the `main/BUILD` file:
```python
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
```
4. Create a `.bazelrc` file at the root of the workspace directory with the
following contents to enable the use of the `--config` flag:
```
# Use our custom-configured c++ toolchain.
build:clang_config --crosstool_top=//toolchain:clang_suite
# Use --cpu as a differentiator.
build:clang_config --cpu=k8
# 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
```
For an entry `build:{config_name} --flag=value`, the command line flag
`--config={config_name}` will be associated with that particular flag. See
documentation for the flags used:
[crosstool_top](../user-manual.html#flag--crosstool_top),
[cpu](../user-manual.html#flag--cpu) and
[host_crosstool_top](../user-manual.html#flag--host_crosstool_top).
What this means is that when we build our [target](../build-ref.html#targets)
with `bazel build --config=clang_config //main:hello-world` Bazel will use our
custom toolchain from the
[cc_toolchain_suite](../be/c-cpp.html#cc_toolchain_suite)
`//toolchain:clang_suite`. The suite may list different [toolchains](../be/c-cpp.html#cc_toolchain)
for different CPUs, that's why we differentiate with the flag `--cpu=k8`.
Since Bazel uses many internal tools written in
C++ during the build, such as process-wrapper, we are specifying the
pre-existing default C++ toolchain for the host platform, so that these tools
are built using that toolchain instead of the one created in this tutorial.
## Configuring the C++ toolchain
To configure the C++ toolchain, repeatedly build the application and eliminate
each error one by one as described below.
**Note:** This tutorial assumes you're using Bazel 0.23 or later. If you're
using an older release of Bazel, look for the "Configuring CROSSTOOL" tutorial.
It also assumes `clang version 9.0.1`, although the details should only change
slightly between different versions of clang.
1. Run the build with the following command:
```
bazel build --config=clang_config //main:hello-world
```
Because you specified `--crosstool_top=//toolchain:clang_suite` in the
`.bazelrc` file, Bazel throws the following error:
```
No such package `toolchain`: BUILD file not found on package path.
```
In the workspace directory, create the `toolchain` directory for the package
and an empty `BUILD` file inside the `toolchain` directory.
2. Run the build again. Because the `toolchain` package does not yet define the
`clang_suite` target, Bazel throws the following error:
```
No such target '//toolchain:clang_suite': target 'clang_suite' not declared
in package 'toolchain' defined by .../toolchain/BUILD
```
In the `toolchain/BUILD` file, define an empty filegroup as follows:
```python
package(default_visibility = ["//visibility:public"])
filegroup(name = "clang_suite")
```
3. Run the build again. Bazel throws the following error:
```
'//toolchain:clang_suite' does not have mandatory providers: 'ToolchainInfo'
```
Bazel discovered that the `--crosstool_top` flag points to a rule that
doesn't provide the necessary [`ToolchainInfo`](../skylark/lib/ToolchainInfo.html)
provider. So we need to point `--crosstool_top` to a rule that does provide
`ToolchainInfo` - that is the `cc_toolchain_suite` rule. In the
`toolchain/BUILD` file, replace the empty filegroup with the following:
```python
cc_toolchain_suite(
name = "clang_suite",
toolchains = {
"k8": ":k8_toolchain",
},
)
```
The `toolchains` attribute automatically maps the `--cpu` (and also
`--compiler` when specified) values to `cc_toolchain`. You have not yet
defined any `cc_toolchain` targets and Bazel will complain about that
shortly.
4. Run the build again. Bazel throws the following error:
```
Rule '//toolchain:k8_toolchain' does not exist
```
Now you need to define `cc_toolchain` targets for every value in the
`cc_toolchain_suite.toolchains` attribute. Add the following to the
`toolchain/BUILD` file:
```python
filegroup(name = "empty")
cc_toolchain(
name = "k8_toolchain",
toolchain_identifier = "k8-toolchain",
toolchain_config = ":k8_toolchain_config",
all_files = ":empty",
compiler_files = ":empty",
dwp_files = ":empty",
linker_files = ":empty",
objcopy_files = ":empty",
strip_files = ":empty",
supports_param_files = 0,
)
```
5. Run the build again. Bazel throws the following error:
```
Rule '//toolchain:k8_toolchain_config' does not exist
```
Let's add a ":k8_toolchain_config" target to the `toolchain/BUILD` file:
```python
filegroup(name = "k8_toolchain_config")
```
6. Run the build again. Bazel throws the following error:
```
'//toolchain:k8_toolchain_config' does not have mandatory providers:
'CcToolchainConfigInfo'
```
`CcToolchainConfigInfo` is a provider that we use to configure our C++
toolchains. We are going to create a Starlark rule that will provide
`CcToolchainConfigInfo`. Create a `toolchain/cc_toolchain_config.bzl`
file with the following content:
```python
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()` creates the needed provider
`CcToolchainConfigInfo`. Now let's declare a rule that will make use of
the newly implemented `cc_toolchain_config` rule. Add a load statement to
`toolchains/BUILD`:
```python
load(":cc_toolchain_config.bzl", "cc_toolchain_config")
```
And replace the "k8_toolchain_config" filegroup with a declaration of a
`cc_toolchain_config` rule:
```python
cc_toolchain_config(name = "k8_toolchain_config")
```
7. Run the build again. Bazel throws the following error:
```
.../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`
```
At this point, Bazel has enough information to attempt building the code but
it still does not know what tools to use to complete the required build
actions. We will modify our Starlark rule implementation to tell Bazel what
tools to use. For that, we'll need the tool_path() constructor from
[`@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl`](https://source.bazel.build/bazel/+/4eea5c62a566d21832c93e4c18ec559e75d5c1ce:tools/cpp/cc_toolchain_config_lib.bzl;l=400):
```python
# 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
)
```
Make sure that `/usr/bin/clang` and `/usr/bin/ld` are the correct paths
for your system.
8. Run the build again. Bazel throws the following error:
```
..../BUILD:3:1: undeclared inclusion(s) in rule '//main:hello-world':
this rule is missing dependency declarations for the following files included by 'main/hello-world.cc':
'/usr/include/c++/9/ctime'
'/usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h'
'/usr/include/x86_64-linux-gnu/c++/9/bits/os_defines.h'
....
```
Bazel needs to know where to search for included headers. There are
multiple ways to solve this like using the `includes` attribute of
`cc_binary`, but here we will solve it at the toolchain level with the
[`cxx_builtin_include_directories`](../skylark/lib/cc_common.html#create_cc_toolchain_config_info)
parameter of `cc_common.create_cc_toolchain_config_info`. Beware that if
you are using a different version of `clang`, the include path will be
different. These paths may also be different depending on the distribution.
Modify the return value in `toolchain/cc_toolchain_config.bzl` to look
like this:
```python
return cc_common.create_cc_toolchain_config_info(
ctx = ctx,
cxx_builtin_include_directories = [ # NEW
"/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,
)
```
9. Run the build command again, you will see an error like:
```
/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'
```
The reason for this is because the linker is missing the C++ standard library
and it can't find its symbols. There are many ways to solve this, like using
the `linkopts` attribute of `cc_binary`. Here we will solve it making sure
that any target using our toolchain doesn't have to specify this flag. Copy
the following code to `cc_toolchain_config.bzl`.
```python
# 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",
"flag_group",
"flag_set",
"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],
)
```
10. If you run `bazel build --config=clang_config //main:hello-world`, it should
finally build.
In this tutorial you have learned how to configure a basic C++ toolchain. But
toolchains are much more powerful than this simple example. You can visit
[C++ toolchain configuration](../cc-toolchain-config-reference.html)
to learn more about them.
The key take-aways are:
- You need to specify a `--crosstool_top` flag in the command line which should
point to a `cc_toolchain_suite`
- You can create a shortcut for a particular configuration using the `.bazelrc`
file
- The cc_toolchain_suite may list `cc_toolchains` for different CPUs and
compilers. You can use command line flags like `--cpu` to differentiate.
- You have to let the toolchain know where the tools live. In this tutorial
we have a simplified version where we access the tools from the system. If you
are interested in a more self-contained approach you can read about workspaces
[here](../be/workspace.html). Your tools
could come from a different workspace and you would have to make their files
available to the `cc_toolchain` via target dependencies on attributes like
`compiler_files`. The `tool_paths` would need to be changed as well.
- You can create features to customize which flags should be passed to different
actions, be it linking or any other type of action.
|