xref: /aosp_15_r20/external/pigweed/pw_toolchain_bazel/get_started.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_toolchain_bazel-get-started:
2
3===================================
4Get started with pw_toolchain_bazel
5===================================
6.. pigweed-module-subpage::
7   :name: pw_toolchain_bazel
8
9-----------
10Quick start
11-----------
12The fastest way to get started using ``pw_toolchain_bazel`` is to use Pigweed's
13upstream toolchains.
14
151. Enable required features in your project's ``//.bazelrc`` file:
16
17   .. code-block:: sh
18
19      # Required for new toolchain resolution API.
20      build --incompatible_enable_cc_toolchain_resolution
21
22      # Do not attempt to configure an autodetected (local) toolchain.
23      common --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
24
252. Configure ``pw_toolchain_bazel`` in your project's ``//WORKSPACE`` file:
26
27   .. code-block:: py
28
29      # Add Pigweed itself, as a submodule from `//third_party/pigweed`.
30      #
31      # TODO: b/300695111 - Support depending on Pigweed as a git_repository,
32      # even if you use pw_toolchain.
33      local_repository(
34          name = "pigweed",
35          path = "third_party/pigweed",
36      )
37      local_repository(
38          name = "pw_toolchain",
39          path = "third_party/pigweed/pw_toolchain_bazel",
40      )
41
42      # Set up CIPD.
43      load(
44          "@pigweed//pw_env_setup/bazel/cipd_setup:cipd_rules.bzl",
45          "cipd_client_repository",
46          "cipd_repository",
47      )
48
49      cipd_client_repository()
50
51      # Set up and register Pigweed's toolchains.
52      load(
53          "@pigweed//pw_toolchain:register_toolchains.bzl",
54          "register_pigweed_cxx_toolchains"
55      )
56
57      register_pigweed_cxx_toolchains()
58
59And you're done! You should now be able to compile for macOS, Linux, and ARM
60Cortex-M devices.
61
62.. _module-pw_toolchain_bazel-get-started-overview:
63
64--------
65Overview
66--------
67This guide shows you how to use ``pw_toolchain_bazel`` to assemble a fully
68working toolchain. There are three core elements in a C/C++ toolchain in
69Bazel:
70
71#. The underlying tools used to perform compile and link actions.
72#. Flag declarations that may or may not apply to a given toolchain
73   configuration.
74#. The final toolchain definition that binds tools and flag declarations
75   together to produce working C/C++ compile and link commands.
76
77This guide assumes you have a good grasp on writing Bazel build files, and also
78assumes you have a working understanding of what flags are typically passed to
79various compile and link tool invocations.
80
81--------------------------------
82Adding Pigweed to your WORKSPACE
83--------------------------------
84Before you can use Pigweed and ``pw_toolchain_bazel`` in your project, you must
85register Pigweed in your ``//WORKSPACE`` file:
86
87.. code-block:: py
88
89   # Add Pigweed itself, as a submodule from `//third_party/pigweed`.
90   #
91   # TODO: b/300695111 - Support depending on Pigweed as a git_repository,
92   # even if you use pw_toolchain.
93   local_repository(
94       name = "pigweed",
95       path = "third_party/pigweed",
96   )
97   local_repository(
98       name = "pw_toolchain",
99       path = "third_party/pigweed/pw_toolchain_bazel",
100   )
101
102.. admonition:: Note
103
104   `b/300695111 <https://issues.pigweed.dev/300695111>`_\: You must add Pigweed
105   as a submodule to use Pigweed in a Bazel project. Pigweed does not yet work
106   when added as a ``http_repository``.
107
108------------------
109Configure .bazelrc
110------------------
111To use this module's toolchain rules, you must first add a couple
112flags that tell Bazel how to find toolchain definitions. Bazel's ``.bazelrc``
113lives at the root of your project, and is the source of truth for your
114project-specific build flags that control Bazel's behavior.
115
116.. code-block:: sh
117
118   # Required for new toolchain resolution API.
119   build --incompatible_enable_cc_toolchain_resolution
120
121   # Do not attempt to configure an autodetected (local) toolchain. We vendor
122   # all our toolchains, and CI VMs may not have any local toolchain to detect.
123   common --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
124
125.. _module-pw_toolchain_bazel-assemble-a-tool-suite:
126
127---------------------
128Assemble a tool suite
129---------------------
130The fastest way to get started is using a toolchain tool repository template.
131``pw_toolchain_bazel`` provides pre-assembled templates for ``clang`` and
132``arm-none-eabi-gcc`` toolchains in the
133`@pw_toolchain//build_external <https://cs.opensource.google/pigweed/pigweed/+/main:pw_toolchain_bazel/build_external/>`_
134package. These build files can be attached to an external repository in your
135``WORKSPACE`` file using the ``build_file`` attribute of ``http_archive``,
136``git_repository``, or ``cipd_repository``.
137
138.. code-block:: py
139
140   # Declare a toolchain tool suite for Linux.
141   http_archive(
142       name = "linux_clang_toolchain",
143       build_file = "@pw_toolchain//build_external:llvm_clang_legacy.BUILD",
144       sha256 = "884ee67d647d77e58740c1e645649e29ae9e8a6fe87c1376be0f3a30f3cc9ab3",
145       strip_prefix = "clang+llvm-17.0.6-x86_64-linux-gnu-ubuntu-22.04",
146       url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.6/clang+llvm-17.0.6-x86_64-linux-gnu-ubuntu-22.04.tar.xz",
147   )
148
149---------------------------
150Create toolchain definition
151---------------------------
152To set up a complete toolchain definition, you'll need ``toolchain`` and
153``pw_cc_toolchain`` rules that serve as the core of your toolchain.
154A simplified example is provided below.
155
156.. code-block:: py
157
158   load("@pw_toolchain//cc_toolchain:defs.bzl", "pw_cc_toolchain")
159
160   pw_cc_toolchain(
161       name = "host_toolchain",
162       action_configs = [
163           "@linux_clang_toolchain//:ar",
164           "@linux_clang_toolchain//:clang",
165           "@linux_clang_toolchain//:clang++",
166           "@linux_clang_toolchain//:lld",
167           "@linux_clang_toolchain//:llvm-cov",
168           "@linux_clang_toolchain//:llvm-objcopy",
169           "@linux_clang_toolchain//:llvm-objdump",
170           "@linux_clang_toolchain//:llvm-strip",
171       ],
172       cxx_builtin_include_directories = [
173           "%package(@linux_clang_toolchain//)%/include/x86_64-unknown-linux-gnu/c++/v1",
174           "%package(@linux_clang_toolchain//)%/include/c++/v1",
175           "%package(@linux_clang_toolchain//)%/lib/clang/17/include",
176       ],
177       toolchain_identifier = "host-linux-toolchain",
178       flag_sets = [
179           "@pw_toolchain//flag_sets:c++17",
180           "@pw_toolchain//flag_sets:debugging",
181           "@pw_toolchain//flag_sets:no_canonical_prefixes",
182       ],
183   )
184
185   toolchain(
186       name = "host_cc_toolchain_linux",
187       # This is the list of constraints that must be satisfied for the suite of
188       # toolchain tools to be determined as runnable on the current machine.
189       exec_compatible_with = [
190           "@platforms//os:linux",
191       ],
192       # This is the list of constraints that dictates compatibility of the final
193       # artifacts produced by this toolchain.
194       target_compatible_with = [
195           "@platforms//os:linux",
196       ],
197       toolchain = ":host_toolchain",
198       toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
199   )
200
201The ``toolchain`` rule
202======================
203The ``toolchain`` rule communicates to Bazel what kind of toolchains are
204available, what environments the tools can run on, and what environment the
205artifacts are intended for. A quick overview of the critical parts of this
206rule are outlined below.
207
208- ``name``: The name of the toolchain rule. This is the label that you
209  reference when registering a toolchain so Bazel knows it may use this
210  toolchain.
211- ``toolchain_type``: The language this toolchain is designed for. Today,
212  ``pw_toolchain_bazel`` only supports C/C++ toolchains via the
213  ``@bazel_tools//tools/cpp:toolchain_type`` type.
214- ``exec_compatible_with``: What constraints must be satisfied for this
215  toolchain to be compatible with the execution environment. In simpler terms,
216  if the machine that is currently running the build is a Linux x86_64 machine,
217  it can only use toolchains designed to run on that OS and architecture.
218  ``exec_compatible_with`` is what prevents a Linux machine from trying to
219  compile using tools designed for a Windows machine and vice versa.
220- ``target_compatible_with``: What constraints must be satisfied for this
221  toolchain to be compatible with the targeted environment. Rather than
222  specifying whether the *tools* are compatible, this specifies the
223  compatibility of the final artifacts produced by this toolchain.
224  For example, ``target_compatible_with`` is what tells Bazel that a toolchain
225  is building firmware for a Cortex-M4.
226- ``toolchain``: The rule that implements the toolchain behavior. When using
227  ``pw_toolchain_bazel``, this points to a :py:class:`pw_cc_toolchain` rule.
228  Multiple ``toolchain`` rules can point to the same
229  :py:class:`pw_cc_toolchain`, which can be useful for creating parameterized
230  toolchains that have a lot in common.
231
232
233The ``pw_cc_toolchain`` rule
234============================
235This is the heart of your C/C++ toolchain configuration, and has two main
236configuration surfaces of interest.
237
238- :py:attr:`pw_cc_toolchain.action_configs`\: This is a list of bindings that
239  map various toolchain actions to the appropriate tools. Typically you'll just
240  want to list all of the :py:class:`pw_cc_action_config` rules included in your
241  toolchain repository from
242  :ref:`module-pw_toolchain_bazel-assemble-a-tool-suite`. If you need to swap
243  out a particular tool, you can just create a custom
244  :py:class:`pw_cc_tool` and :py:class:`pw_cc_action_config` and list it here.
245- :py:attr:`pw_cc_toolchain.flag_sets`\: This lists all the flags
246  that are applied when compiling with this toolchain. Each
247  :py:class:`pw_cc_flag_set` listed here includes at least one flag that applies
248  to at least one kind of action.
249
250While the other attributes of a :py:class:`pw_cc_toolchain` are still required,
251their behaviors are less interesting from a configuration perspective and are
252required for correctness and completeness reasons. See the full
253API reference for :py:class:`pw_cc_toolchain` for more information.
254
255-----------------------
256Register your toolchain
257-----------------------
258Once you've declared a complete toolchain to your liking, you'll need to
259register it in your project's ``WORKSPACE`` file so Bazel knows it can use the
260new toolchain. An example for a ``toolchain`` with the name
261``host_cc_toolchain_linux`` living in ``//toolchains/BUILD`` is illustrated
262below.
263
264.. code-block:: py
265
266   register_toolchains(
267        "//toolchains:host_cc_toolchain_linux",
268   )
269
270At this point, you should have a custom, working toolchain! For more extensive
271examples, consider taking a look at Pigweed's
272`fully instantiated and supported toolchains <https://cs.opensource.google/pigweed/pigweed/+/main:pw_toolchain/host_clang/BUILD.bazel>`_
273
274---------------------------------
275Customize behavior with flag sets
276---------------------------------
277Now that your toolchain is working, you can customize it by introducing new flag
278sets.
279
280Configure warnings
281==================
282Enabling compiler warnings and setting them to be treated as errors is a great
283way to prevent unintentional bugs that stem from dubious code.
284
285.. code-block:: py
286
287   load(
288       "@pw_toolchain//cc_toolchain:defs.bzl",
289       "pw_cc_flag_set",
290   )
291
292   pw_cc_flag_set(
293       name = "warnings",
294       actions = [
295           "@pw_toolchain//actions:all_c_compiler_actions",
296           "@pw_toolchain//actions:all_cpp_compiler_actions",
297       ],
298       flags = [
299           "-Wall",
300           "-Wextra",
301           "-Werror",  # Make all warnings errors, except for the exemptions below.
302           "-Wno-error=cpp",  # preprocessor #warning statement
303           "-Wno-error=deprecated-declarations",  # [[deprecated]] attribute
304       ],
305   )
306
307Omit unreferenced symbols
308=========================
309If a function, variable, or data section isn't used anywhere in your binaries,
310it can be omitted with the following flag sets.
311
312.. code-block:: py
313
314   load(
315       "@pw_toolchain//cc_toolchain:defs.bzl",
316       "pw_cc_flag_set",
317   )
318
319   # Treats symbols representing functions and data as individual sections.
320   # This is mostly relevant when using `:omit_unused_sections`.
321   pw_cc_flag_set(
322       name = "function_and_data_sections",
323       actions = [
324           "@pw_toolchain//actions:all_c_compiler_actions",
325           "@pw_toolchain//actions:all_cpp_compiler_actions",
326       ],
327       flags = [
328           "-ffunction-sections",
329           "-fdata-sections",
330       ],
331   )
332
333   pw_cc_flag_set(
334       name = "omit_unused_sections",
335       actions = ["@pw_toolchain//actions:all_link_actions"],
336       # This flag is parameterized by operating system. macOS and iOS require
337       # a different flag to express this concept.
338       flags = select({
339           "@platforms//os:macos": ["-Wl,-dead_strip"],
340           "@platforms//os:ios": ["-Wl,-dead_strip"],
341           "//conditions:default": ["-Wl,--gc-sections"],
342       }),
343   )
344
345Set global defines
346==================
347Toolchains may declare preprocessor defines that are available for all compile
348actions.
349
350.. code-block:: py
351
352   load(
353       "["@pw_toolchain//cc_toolchain:defs.bzl"]",
354       "pw_cc_flag_set",
355   )
356
357   # Specify global defines that should be available to all compile actions.
358   pw_cc_flag_set(
359      name = "global_defines",
360      actions = [
361          "@pw_toolchain//actions:all_asm_compiler_actions",
362          "@pw_toolchain//actions:all_c_compiler_actions",
363          "@pw_toolchain//actions:all_cpp_compiler_actions",
364      ],
365      flags = [
366         "-DPW_LOG_LEVEL=PW_LOG_LEVEL_INFO",  # Omit all debug logs.
367      ],
368   )
369
370Bind custom flags to a toolchain
371================================
372After you've assembled a selection of custom flag sets, you can bind them to
373your toolchain definition by listing them in
374:py:attr:`pw_cc_toolchain.flag_sets`\:
375
376.. code-block:: py
377   :emphasize-lines: 12,13,14,15
378
379   pw_cc_toolchain(
380       name = "host_toolchain",
381       action_configs = [
382           "@linux_clang_toolchain//:ar",
383           "@linux_clang_toolchain//:clang",
384           "@linux_clang_toolchain//:clang++",
385       ...
386       flag_sets = [
387           "@pw_toolchain//flag_sets:c++17",
388           "@pw_toolchain//flag_sets:debugging",
389           "@pw_toolchain//flag_sets:no_canonical_prefixes",
390           ":warnings",  # Newly added pw_cc_flag_set from above.
391           ":function_and_data_sections",  # Newly added pw_cc_flag_set from above.
392           ":omit_unused_sections",  # Newly added pw_cc_flag_set from above.
393           ":global_defines",  # Newly added pw_cc_flag_set from above.
394       ],
395   )
396
397.. admonition:: Note
398
399   Flags appear in the tool invocations in the order as they are listed
400   in :py:attr:`pw_cc_toolchain.flag_sets`\, so if you need a flag
401   to appear earlier in the command-line invocation of the tool just move it to
402   towards the beginning of the list.
403
404You can use ``pw_cc_flag_set`` rules to add support for new architectures,
405enable/disable warnings, add preprocessor defines, enable LTO, and more.
406