blob: 08799faf74d4bfe8fbdd706b791e932b1596ac87 [file]
.. _module-pw_toolchain-bazel:
===============================
Bazel build system integrations
===============================
Pigweed provides a suite of Bazel build integrations to compliment existing
Bazel toolchain constructs such as `rules_cc toolchains <https://github.com/bazelbuild/rules_cc/blob/main/cc/toolchains/README.md>`_
to make it easier to design robust, feature-rich toolchains.
.. _module-pw_toolchain-bazel-upstream-pigweed-toolchains:
---------------------------
Upstream Pigweed toolchains
---------------------------
Pigweed's C/C++ toolchains can be registered from a bzlmod project by adding
the following to your ``MODULE.bazel``:
.. code-block:: py
register_toolchains(
"@pigweed//pw_toolchain:cc_toolchain_cortex-m0",
"@pigweed//pw_toolchain:cc_toolchain_cortex-m0plus",
"@pigweed//pw_toolchain:cc_toolchain_cortex-m33",
"@pigweed//pw_toolchain/arm_gcc:arm_gcc_cc_toolchain_cortex-m3",
"@pigweed//pw_toolchain/arm_gcc:arm_gcc_cc_toolchain_cortex-m4",
"@pigweed//pw_toolchain/host_clang:host_cc_toolchain_linux",
"@pigweed//pw_toolchain/host_clang:host_cc_toolchain_macos",
"@pigweed//pw_toolchain/riscv_clang:riscv_clang_cc_toolchain_rv32imc",
"@pigweed//pw_toolchain/riscv_clang:riscv_clang_cc_toolchain_rv32imac",
dev_dependency = True,
)
If you create custom ARM or RISC-V toolchains, you may want to remove
Pigweed's device-specific toolchains to avoid them accidentally getting selected
for your device build.
.. admonition:: Note
:class: warning
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.
Injecting custom flags
======================
If you wish to add extra arguments to Pigweed's toolchains, you can set
the ``--@pigweed//pw_toolchain/cc/args:extra_toolchain_args`` label flag
to point at a ``cc_args`` or ``cc_args_list`` rule that contains the flags
you'd like to add.
For platform-specific flags, you can set this flag in your ``platform`` flags
rather than in a ``.bazelrc`` file.
.. _module-pw_toolchain-bazel-layering-check:
Layering check
==============
Upstream Pigweed toolchains have support for `layering check
<https://maskray.me/blog/2022-09-25-layering-check-with-clang>`__. 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.
.. admonition:: 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
<https://bazel.build/external/overview#repo.bazel>`__ file at the root of
your project, with the following content:
.. code-block:: py
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
<https://github.com/bazelbuild/buildtools/blob/main/buildozer/README.md>`__
one-liner:
.. code-block:: console
buildozer 'add features -layering_check' '//...:__pkg__'
Note the ``-`` in front of ``layering_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
<https://bazel.build/reference/be/common-definitions#common.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 the
``parse_headers`` feature. Pigweed's toolchains do not yet contain its
implementation. Adding it is tracked at :bug:`391367050`.
#. Layering check will not prevent you from using symbols from transitively
included headers. For this, use `misc-include-cleaner
<https://clang.llvm.org/extra/clang-tidy/checks/misc/include-cleaner.html>`__
in clang-tidy. See also :bug:`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 :bug:`391394448`.
.. _module-pw_toolchain-bazel-clang-tidy:
clang-tidy
==========
To integrate Pigweed's toolchain with `bazel_clang_tidy
<https://github.com/erenon/bazel_clang_tidy>`_:
#. Add a ``.clang-tidy`` file at the root of your repository listing the checks
you wish to enable. :cs:`Pigweed's own .clang-tidy file <main:.clang-tidy>`
shows some checks we recommend.
#. Create a ``filegroup`` target containing that file in ``BUILD.bazel`` at
the root of your repo.
.. code-block:: python
filegroup(
name = "clang_tidy_config",
srcs = [".clang-tidy"],
)
#. Add `bazel_clang_tidy`_ to your ``MODULE.bazel``.
.. code-block::python
git_repository = use_repo_rule(
"@bazel_tools//tools/build_defs/repo:git.bzl",
"git_repository",
)
git_repository(
name = "bazel_clang_tidy",
# Check the repository for the latest version!
commit = "db677011c7363509a288a9fb3bf0a50830bbf791",
remote = "https://github.com/erenon/bazel_clang_tidy.git",
)
#. Add a ``clang-tidy`` config in your ``.bazelrc`` file.
.. code-block:: python
# 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 <http://pwrev.dev/266934>`__.
Conversion warnings
===================
By default, upstream Pigweed is built with `-Wconversion
<https://clang.llvm.org/docs/DiagnosticsReference.html#wconversion>`__ enabled.
However, this was not always the case, and many Pigweed targets contain
``-Wconversion`` violations. (:bug:`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:
.. code-block:: py
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
<https://bazel.build/external/overview#repo.bazel>`__ file at the root of
your project, with the following content:
.. code-block:: py
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.
.. _module-pw_toolchain-bazel-running-tools:
-----------------------
Running toolchain tools
-----------------------
Pigweed provides a set of runnable targets that expose the active toolchain's
tools for interactive use. These are essential for debugging and inspecting
build artifacts, as local system tools often lack support for the target
architecture of a cross-compiled build. Furthermore, since Pigweed's compilers
are typically hermetic and managed by Bazel, their binaries are stored within
Bazel's internal directories and are not easily accessible through the shell.
Available tools include ``cc``, ``c++``, ``ld``, ``ar``, ``objdump``, ``nm``,
``readelf``, ``size``, ``strip``, and ``cov``.
These targets are dynamically resolved from the active toolchain's action
mapping. This ensures that when you run a tool via these targets, you are using
the *exact same* binary and configuration that Bazel uses during the build
for that specific target platform.
.. note::
Tool availability varies by suite (e.g., LLVM vs. GCC vs.
Zephyr-Arm vs. Zephyr-LLVM) and configuration. While common tools
like ``objdump`` and ``nm`` are present across all supported suites
(including ARM GCC and Zephyr), others like ``cov`` or ``gcov``
may be missing. Attempting to run a missing tool will result in a
"tool not found" error or fail with a clear error message
during analysis or execution.
Additionally, compiler drivers that are symlinks to a multicall binary
(like ``llvm``) may behave as a generic driver if invoked without
arguments.
.. code-block:: console
# Disassemble a binary using the objdump provided by the toolchain
$ bazel run //pw_toolchain/cc/current_toolchain:objdump -- -d bazel-bin/my_binary
# Link object files manually (for debugging)
$ bazel run //pw_toolchain/cc/current_toolchain:ld -- ...
# List symbols
$ bazel run //pw_toolchain/cc/current_toolchain:nm -- bazel-bin/my_binary
.. admonition:: Warning
:class: warning
These targets are intended **exclusively** for interactive use via ``bazel run``.
**Do NOT** use them as dependencies in other rules (e.g. in ``srcs``,
``tools``, or ``deps``).
Doing so will likely result in the wrong tool being selected (e.g. the host
``objdump`` instead of the target ``objdump``) due to Bazel's configuration
transition logic.
.. _module-pw_toolchain-bazel-compiler-specific-logic:
-----------------------------
Compiler-specific build logic
-----------------------------
Whenever possible, avoid introducing compiler-specific behaviors in Bazel
``BUILD`` files. Instead, prefer to design build logic against
more intentional :ref:`docs-bazel-compatibility`. For compiler-specific
behavior, this means defining and/or using compiler capabilities like
`@rules_cc//cc/toolchains/capabilities:supports_interface_shared_libraries <https://github.com/bazelbuild/rules_cc/blob/main/cc/toolchains/capabilities/BUILD>`__
If you need to expose a toolchain capability as a choice in a select, you
can use ``pw_cc_toolchain_feature_is_enabled``.
Example:
.. code-block:: py
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:
.. code-block:: py
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"],
)
---------------------------------
Compiler-specific toolchain flags
---------------------------------
In cases where foundationally different toolchains (e.g. Clang, GCC, MSVC) share
large pieces of project-wide configuration, you may want to conditionally add
flags that are compiler-specific. This can be done with the following steps:
#. **Express the type of compiler in your cc_toolchain.** This is how the
rest of your toolchain rules will know what kind of compiler is active.
.. code-block:: py
cc_toolchain(
name = "arm_clang_toolchain_cortex-a",
# ...
enabled_features = [
"@pigweed//pw_toolchain/cc/capability:compiler_is_clang",
"@pigweed//pw_toolchain/cc/capability:linker_is_clang",
],
)
#. **Add the list of known toolchain types to your toolchain.** This ensures
that the ``cc_toolchain`` passes feature correctness validations.
.. code-block:: py
cc_toolchain(
name = "arm_clang_toolchain_cortex-a",
# ...
enabled_features = [
"@pigweed//pw_toolchain/cc/capability:compiler_is_clang",
"@pigweed//pw_toolchain/cc/capability:linker_is_clang",
],
known_features = [
"//pw_toolchain/cc/capability:known_toolchain_types",
],
)
#. **Gate the arguments with requires_any_of.** ``cc_args`` gated by
a ``requires_any_of`` constraint on a toolchain type will only be expanded
in the compiler/linker invocation if the toolchain tool matches the required
type. Keep in mind that ``compiler_is_clang`` and ``linker_is_clang`` types
are offered separately to support cases where the compiler and linker types
are not the same.
.. code-block:: py
cc_args(
name = "clang_only_extra_pigweed_warnings",
actions = [
"@rules_cc//cc/toolchains/actions:compile_actions",
],
requires_any_of = [
"//pw_toolchain/cc/capability:compiler_is_clang",
],
args = [
"-Wshadow-all",
],
)