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!

As an example of this setup, see the CL that added clang-tidy support to our Quickstart repo.

Conversion warnings#

By default, upstream Pigweed is built with -Wconversion enabled. However, this was not always the case, and many Pigweed targets contain -Wconversion violations. (b/259746255 tracks fixing all of these.)

Upstream allowlist#

Do not add new -Wconversion violations to the Pigweed codebase.

If you write new code that fails to build because it includes a header with a pre-existing -Wconversion violation, try to fix the pre-existing violation.

As a last resort, you may add the features = ["-conversion_warnings"] (note the -!) attribute to your cc_library or other build target:

cc_library(
   name = "…",
   features = ["-conversion_warnings"],
)

This will disable -Wconversion for this target.

Downstream use#

If you would like to enable -Wconversion in a downstream project that uses Pigweed’s toolchains, add a REPO.bazel file at the root of your project, with the following content:

repo(
    features = ["conversion_warnings"],
)

This will enable -Wconversion for all code in your project, but not for code coming from any external dependencies also built with Bazel.

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"],
        otherwise = [],
    ),
    linkopts = if_linker_is_gcc(
        ["-Wl,--delete-main"],
        otherwise = [],
    ),
    srcs = ["lib.cc"],
)