Bazel build system integrations#
Pigweed provides a suite of Bazel build integrations to compliment existing Bazel toolchain constructs such as rules_cc toolchains to make it easier to design robust, feature-rich toolchains.
Upstream Pigweed toolchains#
Pigweed’s C/C++ toolchains are automatically registered when using Pigweed from
a Bzlmod Bazel project. Legacy WORKSPACE-based projects can use Pigweed’s
upstream toolchains by calling register_pigweed_cxx_toolchains()
:
load("@pigweed//pw_toolchain:register_toolchains.bzl", "register_pigweed_cxx_toolchains")
register_pigweed_cxx_toolchains()
Note
Pigweed’s upstream toolchains are subject to change without notice. If you would prefer more stability in toolchain configurations, consider declaring custom toolchains in your project.
Layering check#
Upstream Pigweed toolchains have support for layering check. In short,
enabling layering check makes it a compile-time error to #include
a header
that’s not in the hdrs
of a cc_library
you directly depend on. This
produces cleaner dependency graphs and is recommended for all users.
Note
Layering check requires Bazel 8.0.0 or newer.
How to enable layering check?#
Add
common --@pigweed//pw_toolchain/host_clang:layering_check
to your.bazelrc
. This does not by itself enable the check, it only instructs Bazel to include support for it in the toolchain configuration. (This flag will become true by default and be removed once all known Pigweed users are on Bazel 8.)Enable the
layering_check
feature to enable enforcement. The recommended way to do this is to add a REPO.bazel file at the root of your project, with the following content:repo( features = ["layering_check"], )
This will enable
layering_check
for all code in your project, but not for code coming from any external dependencies also built with Bazel.
Gradual rollout#
When you enable layering check for the first time, you will likely get a very
large number of errors, since many packages in your project will contain
layering violations. If there are too many errors to fix at once, here’s one way
to roll out layering_check
gradually:
Enable the
layering_check
feature for the entire repo, as discussed above.In the same commit, disable layering check for each individual package (
BUILD.bazel
file). This can be done using the following buildozer one-liner:buildozer 'add features -layering_check' '//...:__pkg__'
Note the
-
in front oflayering_check
in the command above!Re-enable layering check package by package by removing the lines added by buildozer. When no
-layering_check
remains in your codebase, you’ve enabled layering check for the entire repo!
You can also enable or disable the layering_check
feature for individual
targets, using the features
attribute.
Limitations#
Layering check only applies to
#include
statements in source files, not in header files. To also apply it to header files, you need theparse_headers
feature. Pigweed’s toolchains do not yet contain its implementation. Adding it is tracked at b/391367050.Layering check will not prevent you from using symbols from transitively included headers. For this, use misc-include-cleaner in clang-tidy. See also b/329671260.
A pattern we use for swapping header implementations using a label flag leads to layering check violations. Figuring out an alternative pattern is tracked at b/391394448.
clang-tidy#
To integrate Pigweed’s toolchain with bazel_clang_tidy:
Add a
.clang-tidy
file at the root of your repository listing the checks you wish to enable. Pigweed’s own .clang-tidy file shows some checks we recommend.Create a
filegroup
target containing that file inBUILD.bazel
at the root of your repo.filegroup( name = "clang_tidy_config", srcs = [".clang-tidy"], )
Add bazel_clang_tidy to your
MODULE.bazel
.Add a
clang-tidy
config in your.bazelrc
file.# clang-tidy configuration build:clang-tidy --aspects @bazel_clang_tidy//clang_tidy:clang_tidy.bzl%clang_tidy_aspect build:clang-tidy --output_groups=report build:clang-tidy --@bazel_clang_tidy//:clang_tidy_config=//:clang_tidy_config # Use the clang-tidy executable from Pigweed's toolchain, and include # our sysroot headers. build:clang-tidy --@bazel_clang_tidy//:clang_tidy_executable=@pigweed//pw_toolchain/host_clang:copy_clang_tidy build:clang-tidy --@bazel_clang_tidy//:clang_tidy_additional_deps=@pigweed//pw_toolchain/host_clang:sysroot_root # Skip any targets with tags = ["noclangtidy"]. This allows a gradual # rollout. build:clang-tidy --build_tag_filters=-noclangtidy # We need to disable this warning to avoid spurious "#pragma once in main file" # warnings for header-only libraries. For another approach, see # https://github.com/mongodb-forks/bazel_clang_tidy/pull/2 build:clang-tidy --copt=-Wno-pragma-once-outside-header
Now bazelisk build --config=clang-tidy //...
will run clang-tidy for all
cc_library
targets in your repo!
Compiler-specific build logic#
Whenever possible, avoid introducing compiler-specific behaviors in Bazel
BUILD
files. Instead, prefer to design build logic against
more intentional Bazel build compatibility patterns. For compiler-specific
behavior, this means defining and/or using compiler capabilities like
@rules_cc//cc/toolchains/capabilities:supports_interface_shared_libraries
If you need to expose a toolchain capability as a choice in a select, you
can use pw_cc_toolchain_feature_is_enabled
.
Example:
load(
"@pigweed//pw_toolchain/cc/current_toolchain:pw_cc_toolchain_feature_is_enabled.bzl",
"pw_cc_toolchain_feature_is_enabled",
)
pw_cc_toolchain_feature_is_enabled(
name = "llvm_libc_enabled",
feature_name = "llvm_libc",
)
cc_library(
name = "libfoo",
deps = select({
":llvm_libc_enabled": ["//foo:llvm_libc_extras"],
"//conditions:default": [],
}),
)
If you absolutely must introduce a select
statement that checks the current
compiler, use Pigweed’s helper macros.
Example:
load(
"@pigweed//pw_toolchain/cc/current_toolchain:conditions.bzl",
"if_compiler_is_clang",
"if_linker_is_gcc",
)
cc_library(
copts = if_compiler_is_clang(
["-fno-codegen"],
default = [],
),
linkopts = if_linker_is_gcc(
["-Wl,--delete-main"],
default = [],
),
srcs = ["lib.cc"],
)