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?#

  1. 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.)

  2. 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:

  1. Enable the layering_check feature for the entire repo, as discussed above.

  2. 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 of layering_check in the command above!

  3. 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#

  1. Layering check only applies to #include statements in source files, not in header files. To also apply it to header files, you need the parse_headers feature. Pigweed’s toolchains do not yet contain its implementation. Adding it is tracked at b/391367050.

  2. 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.

  3. 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:

  1. 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.

  2. Create a filegroup target containing that file in BUILD.bazel at the root of your repo.

    filegroup(
       name = "clang_tidy_config",
       srcs = [".clang-tidy"],
    )
    
  3. Add bazel_clang_tidy to your MODULE.bazel.

  4. 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"],
)