• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _module-pw_toolchain-bazel:
2
3===============================
4Bazel build system integrations
5===============================
6Pigweed provides a suite of Bazel build integrations to compliment existing
7Bazel toolchain constructs such as `rules_cc toolchains <https://github.com/bazelbuild/rules_cc/blob/main/cc/toolchains/README.md>`_
8to make it easier to design robust, feature-rich toolchains.
9
10.. _module-pw_toolchain-bazel-upstream-pigweed-toolchains:
11
12---------------------------
13Upstream Pigweed toolchains
14---------------------------
15Pigweed's C/C++ toolchains are automatically registered when using Pigweed from
16a Bzlmod Bazel project. Legacy WORKSPACE-based projects can use Pigweed's
17upstream toolchains by calling ``register_pigweed_cxx_toolchains()``:
18
19.. code-block:: py
20
21   load("@pigweed//pw_toolchain:register_toolchains.bzl", "register_pigweed_cxx_toolchains")
22
23   register_pigweed_cxx_toolchains()
24
25
26.. admonition:: Note
27   :class: warning
28
29   Pigweed's upstream toolchains are subject to change without notice. If you
30   would prefer more stability in toolchain configurations, consider declaring
31   custom toolchains in your project.
32
33.. _module-pw_toolchain-bazel-layering-check:
34
35Layering check
36==============
37Upstream Pigweed toolchains have support for `layering check
38<https://maskray.me/blog/2022-09-25-layering-check-with-clang>`__. In short,
39enabling layering check makes it a compile-time error to ``#include`` a header
40that's not in the ``hdrs`` of a ``cc_library`` you directly depend on. This
41produces cleaner dependency graphs and is recommended for all users.
42
43.. admonition:: Note
44
45   Layering check requires Bazel 8.0.0 or newer.
46
47How to enable layering check?
48-----------------------------
49#. Add ``common --@pigweed//pw_toolchain/host_clang:layering_check`` to your
50   ``.bazelrc``. This does not by itself enable the check, it only instructs
51   Bazel to include support for it in the toolchain configuration. (This flag
52   will become true by default and be removed once all known Pigweed users are
53   on Bazel 8.)
54#. Enable the ``layering_check`` feature to enable enforcement. The recommended
55   way to do this is to add a `REPO.bazel
56   <https://bazel.build/external/overview#repo.bazel>`__ file at the root of
57   your project, with the following content:
58
59   .. code-block:: py
60
61      repo(
62          features = ["layering_check"],
63      )
64
65   This will enable ``layering_check`` for all code in your project, but not for
66   code coming from any external dependencies also built with Bazel.
67
68Gradual rollout
69---------------
70When you enable layering check for the first time, you will likely get a very
71large number of errors, since many packages in your project will contain
72layering violations. If there are too many errors to fix at once, here's one way
73to roll out ``layering_check`` gradually:
74
75#. Enable the ``layering_check`` feature for the entire repo, as discussed
76   above.
77#. In the same commit, disable layering check for each individual package
78   (``BUILD.bazel`` file). This can be done using the following `buildozer
79   <https://github.com/bazelbuild/buildtools/blob/main/buildozer/README.md>`__
80   one-liner:
81
82   .. code-block:: console
83
84      buildozer 'add features -layering_check' '//...:__pkg__'
85
86   Note the ``-`` in front of ``layering_check`` in the command above!
87
88#. Re-enable layering check package by package by removing the lines added by
89   buildozer. When no ``-layering_check`` remains in your codebase, you've
90   enabled layering check for the entire repo!
91
92You can also enable or disable the ``layering_check`` feature for individual
93targets, using the `features
94<https://bazel.build/reference/be/common-definitions#common.features>`__
95attribute.
96
97Limitations
98-----------
99#. Layering check only applies to ``#include`` statements in source files, not
100   in header files. To also apply it to header files, you need the
101   ``parse_headers`` feature. Pigweed's toolchains do not yet contain its
102   implementation. Adding it is tracked at :bug:`391367050`.
103#. Layering check will not prevent you from using symbols from transitively
104   included headers. For this, use `misc-include-cleaner
105   <https://clang.llvm.org/extra/clang-tidy/checks/misc/include-cleaner.html>`__
106   in clang-tidy.  See also :bug:`329671260`.
107#. A pattern we use for swapping header implementations using a label flag
108   leads to layering check violations. Figuring out an alternative pattern is
109   tracked at :bug:`391394448`.
110
111.. _module-pw_toolchain-bazel-clang-tidy:
112
113clang-tidy
114==========
115To integrate Pigweed's toolchain with `bazel_clang_tidy
116<https://github.com/erenon/bazel_clang_tidy>`_:
117
118#. Add a ``.clang-tidy`` file at the root of your repository listing the checks
119   you wish to enable. `Pigweed's own .clang-tidy file
120   <https://cs.opensource.google/pigweed/pigweed/+/main:.clang-tidy>`__ shows
121   some checks we recommend.
122
123#. Create a ``filegroup`` target containing that file in ``BUILD.bazel`` at
124   the root of your repo.
125
126   .. code-block:: python
127
128      filegroup(
129         name = "clang_tidy_config",
130         srcs = [".clang-tidy"],
131      )
132
133#. Add `bazel_clang_tidy`_ to your ``MODULE.bazel``.
134
135   .. code-block::python
136
137      git_repository = use_repo_rule(
138         "@bazel_tools//tools/build_defs/repo:git.bzl",
139         "git_repository",
140      )
141      git_repository(
142         name = "bazel_clang_tidy",
143         # Check the repository for the latest version!
144         commit = "db677011c7363509a288a9fb3bf0a50830bbf791",
145         remote = "https://github.com/erenon/bazel_clang_tidy.git",
146      )
147
148#. Add a ``clang-tidy`` config in your ``.bazelrc`` file.
149
150   .. code-block:: python
151
152      # clang-tidy configuration
153      build:clang-tidy --aspects @bazel_clang_tidy//clang_tidy:clang_tidy.bzl%clang_tidy_aspect
154      build:clang-tidy --output_groups=report
155      build:clang-tidy --@bazel_clang_tidy//:clang_tidy_config=//:clang_tidy_config
156      # Use the clang-tidy executable from Pigweed's toolchain, and include
157      # our sysroot headers.
158      build:clang-tidy --@bazel_clang_tidy//:clang_tidy_executable=@pigweed//pw_toolchain/host_clang:copy_clang_tidy
159      build:clang-tidy --@bazel_clang_tidy//:clang_tidy_additional_deps=@pigweed//pw_toolchain/host_clang:sysroot_root
160      # Skip any targets with tags = ["noclangtidy"]. This allows a gradual
161      # rollout.
162      build:clang-tidy --build_tag_filters=-noclangtidy
163      # We need to disable this warning to avoid spurious "#pragma once in main file"
164      # warnings for header-only libraries. For another approach, see
165      # https://github.com/mongodb-forks/bazel_clang_tidy/pull/2
166      build:clang-tidy --copt=-Wno-pragma-once-outside-header
167
168Now ``bazelisk build --config=clang-tidy //...`` will run clang-tidy for all
169``cc_library`` targets in your repo!
170
171.. _module-pw_toolchain-bazel-compiler-specific-logic:
172
173-----------------------------
174Compiler-specific build logic
175-----------------------------
176Whenever possible, avoid introducing compiler-specific behaviors in Bazel
177``BUILD`` files. Instead, prefer to design build logic against
178more intentional :ref:`docs-bazel-compatibility`. For compiler-specific
179behavior, this means defining and/or using compiler capabilities like
180`@rules_cc//cc/toolchains/capabilities:supports_interface_shared_libraries <https://github.com/bazelbuild/rules_cc/blob/main/cc/toolchains/capabilities/BUILD>`__
181
182If you need to expose a toolchain capability as a choice in a select, you
183can use ``pw_cc_toolchain_feature_is_enabled``.
184
185Example:
186
187.. code-block:: py
188
189   load(
190       "@pigweed//pw_toolchain/cc/current_toolchain:pw_cc_toolchain_feature_is_enabled.bzl",
191       "pw_cc_toolchain_feature_is_enabled",
192   )
193
194   pw_cc_toolchain_feature_is_enabled(
195       name = "llvm_libc_enabled",
196       feature_name = "llvm_libc",
197   )
198
199   cc_library(
200       name = "libfoo",
201       deps = select({
202           ":llvm_libc_enabled": ["//foo:llvm_libc_extras"],
203           "//conditions:default": [],
204       }),
205   )
206
207If you absolutely must introduce a ``select`` statement that checks the current
208compiler, use Pigweed's helper macros.
209
210Example:
211
212.. code-block:: py
213
214   load(
215       "@pigweed//pw_toolchain/cc/current_toolchain:conditions.bzl",
216       "if_compiler_is_clang",
217       "if_linker_is_gcc",
218   )
219
220   cc_library(
221       copts = if_compiler_is_clang(
222           ["-fno-codegen"],
223           default = [],
224       ),
225       linkopts = if_linker_is_gcc(
226           ["-Wl,--delete-main"],
227           default = [],
228       ),
229       srcs = ["lib.cc"],
230   )
231