xref: /aosp_15_r20/external/pigweed/docs/build_system.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _docs-build-system:
2
3=============
4Build systems
5=============
6Building software for embedded devices is a complex process. Projects often have
7custom toolchains, target different hardware platforms, and require additional
8configuration and post-processing of artifacts.
9
10As a modern embedded framework, Pigweed's goal is to collect these embedded use
11cases into a powerful and flexible build system, then extend it with support for
12modern software development practices.
13
14See :ref:`docs-python-build` for information about Python build automation with
15Pigweed.
16
17.. toctree::
18  :hidden:
19
20  bazel_compatibility
21  python_build
22
23What's in a build system?
24=========================
25A quality build system provides a variety of features beyond compiling code.
26Throughout our experience with embedded development, we've found several build
27features to be especially useful, and designed Pigweed's build system with them
28in mind.
29
30Simple toolchain configuration
31------------------------------
32Embedded projects often use custom build toolchains for their specific hardware.
33Configuring these should be a simple process, both in their initial setup and
34later adjustments.
35
36Multi-target builds
37-------------------
38Virtually every consumer product has firmware that targets different boards or
39MCUs during development. While building for a single board is simple enough, the
40complexity of supporting different targets ranges from changing compiler flags
41to swapping out entire libraries of firmware and drivers. This is often done by
42running multiple builds, configuring each one accordingly. In Pigweed, we've
43designed our build system with first-class multi-target support in mind,
44allowing any number of target configurations to be built simultaneously.
45
46Multi-language support
47----------------------
48Embedded projects are typically written in C, C++, and assembly. However, it is
49possible to have firmware written in other languages, such as Rust.
50Additionally, projects may have host-side tooling written in a wide variety of
51languages. Having all of these build together proves to be a large time saver.
52
53Custom scripting
54----------------
55Embedded projects often require post-processing of build artifacts; these may
56include:
57
58* Extracting ELF sections into a different container
59* Injecting metadata into firmware images
60* Image signing
61* Creating databases of symbols for debugging
62* Extracting string tokens into a database (for example, with
63  :ref:`module-pw_tokenizer`)
64
65These are run as steps during a build, facilitated by the build system.
66
67See also
68^^^^^^^^
69
70* :ref:`module-pw_build-python-action`
71
72Python
73------
74Python is a favorite scripting language of many development teams, and here at
75Pigweed, we're no exception. Much of Pigweed's host-side tooling is written in
76Python. While Python works great for local development, problems can arise when
77scripts need to be packaged and distributed for vendors or factory teams. Having
78proper support for packaging Python within a build system allows teams to focus
79on writing code instead of worrying about distribution.
80
81Size reporting
82--------------
83On embedded devices, memory is everything. Most projects have some sort of
84custom tooling to determine how much flash and RAM space their firmware uses.
85Being able to run size reports as part of a build ensures that they are always
86up-to-date and allows space usage to be tracked over time.
87
88See also
89^^^^^^^^
90
91* :ref:`module-pw_bloat`
92
93Documentation
94-------------
95An oft-neglected part of software development, documentation is invaluable for
96future maintainers of a project. As such, Pigweed has integrated documentation
97which builds alongside its code and combines with other build features, such as
98size reports, to provide high quality, up-to-date references for developers.
99
100See also
101^^^^^^^^
102
103* :ref:`module-pw_docgen`
104
105Unit testing
106------------
107Unit tests are essential to ensure that the functionality of code remains
108consistent as changes are made to avoid accidental regressions. Running unit
109tests as part of a build keeps developers constantly aware of the impact of
110their changes.
111
112Host-side unit tests
113^^^^^^^^^^^^^^^^^^^^
114Though Pigweed targets embedded devices, a lot of its code can be run and tested
115on a host desktop by swapping out backends to host platform libraries. This is
116highly beneficial during development, as it allows tests to consistently run
117without having to go through the process of flashing a device.
118
119Device-side unit tests
120^^^^^^^^^^^^^^^^^^^^^^
121As useful as host-side tests are, they are not sufficient for developing actual
122firmware, and it is critical to run tests on the actual hardware. Pigweed has
123invested into creating a test framework and build integration for running tests
124across physical devices as part of a build.
125
126See also
127^^^^^^^^
128
129* :ref:`module-pw_unit_test`
130* :ref:`module-pw_target_runner`
131
132Bonus: pw watch
133---------------
134In web development, it is common to have a file system watcher listening for
135source file changes and triggering a build for quick iteration. When combined
136with a fast incremental build system, this becomes a powerful feature, allowing
137things such as unit tests and size reports to re-run whenever any dependent
138code is modified.
139
140While initially seen as somewhat of a gimmick, Pigweed's watcher has become a
141staple of Pigweed development, with most Pigweed users having it permanently
142running in a terminal window.
143
144See also
145^^^^^^^^
146
147* :ref:`module-pw_watch`
148
149Pigweed's build systems
150=======================
151Pigweed can be used either as a monolith or à la carte, slotting into an
152existing project. To this end, Pigweed supports multiple build systems, allowing
153Pigweed-based projects to choose the most suitable one for them.
154
155Of the supported build systems, GN is the most full-featured, followed by CMake,
156and finally Bazel.
157
158.. note::
159  A quick note on terminology: the word "target" is overloaded within GN/Bazel (and
160  Pigweed)---it can refer to either a GN/Bazel build target, such as a ``source_set``
161  or ``executable``, or to an output platform (e.g. a specific board, device, or
162  system).
163
164  To avoid confusing the two, we refer to the former as "GN/Bazel targets" and the
165  latter as "Pigweed targets".
166
167.. _docs-build-system-gn:
168
169GN
170--
171A perhaps unfamiliar name, `GN (Generate Ninja)`_ is a meta-build system that
172outputs `Ninja`_ build files, originally designed for use in Chromium. Pigweed
173first experimented with GN after hearing about it from another team, and we
174quickly came to appreciate its speed and simplicity. GN has become Pigweed's
175primary build system; it is used for all upstream development and strongly
176recommended for Pigweed-based projects where possible.
177
178.. _CMake: https://cmake.org/
179.. _Bazel: https://bazel.build/
180.. _GN (Generate Ninja): https://gn.googlesource.com/gn
181.. _Ninja: https://ninja-build.org/
182
183The GN build
184============
185This section describes Pigweed's GN build structure, how it is used upstream,
186build conventions, and recommendations for Pigweed-based projects. While
187containing some details about how GN works in general, this section is not
188intended to be a guide on how to use GN. To learn more about the tool itself,
189refer to the official `GN reference`_.
190
191.. _GN reference: https://gn.googlesource.com/gn/+/HEAD/docs/reference.md
192
193Entrypoint: .gn
194---------------
195The entrypoint to a GN build is the ``.gn`` file, which defines a project's root
196directory (henceforth ``//``).
197
198``.gn`` must point to the location of a ``BUILDCONFIG.gn`` file for the project.
199In Pigweed upstream, this is its only purpose.
200
201Downstream projects may additionally use ``.gn`` to set global overrides for
202Pigweed's build arguments, which apply across all Pigweed targets. For example,
203a project could configure the protobuf libraries that it uses. This is done by
204defining a ``default_args`` scope containing the overrides.
205
206.. code-block::
207
208   # The location of the BUILDCONFIG file.
209   buildconfig = "//BUILDCONFIG.gn"
210
211   # Build arguments set across all Pigweed targets.
212   default_args = {
213     dir_pw_third_party_nanopb = "//third_party/nanopb-0.4.2"
214   }
215
216Configuration: BUILDCONFIG.gn
217-----------------------------
218The file ``BUILDCONFIG.gn`` configures the GN build by defining any desired
219global variables/options. It can be located anywhere in the build tree, but is
220conventionally placed at the root. ``.gn`` points GN to this file.
221
222``BUILDCONFIG.gn`` is evaluated before any other GN files, and variables defined
223within it are placed into GN's global scope, becoming available in every file
224without requiring imports.
225
226The options configured in this file differ from those in ``.gn`` in two ways:
227
2281. ``BUILDCONFIG.gn`` is evaluated for every GN toolchain (and Pigweed target),
229   whereas ``.gn`` is only evaluated once. This allows ``BUILDCONFIG.gn`` to set
230   different options for each Pigweed target.
2312. In ``.gn``, only GN build arguments can be overridden. ``BUILDCONFIG.gn``
232   allows defining arbitrary variables.
233
234Generally, it is preferable to expose configuration options through build args
235instead of globals in ``BUILDCONFIG.gn`` (something Pigweed's build previously
236did), as they are more flexible, greppable, and easier to manage. However, it
237may make sense to define project-specific constants in ``BUILDCONFIG.gn``.
238
239Pigweed's upstream ``BUILDCONFIG.gn`` does not define any variables; it just
240sets Pigweed's default toolchain, which GN requires.
241
242.. _top-level-build:
243
244Top-level GN targets: //BUILD.gn
245--------------------------------
246The root ``BUILD.gn`` file defines all of the libraries, images, tests, and
247binaries built by a Pigweed project. This file is evaluated immediately after
248``BUILDCONFIG.gn``, with the active toolchain (which is the default toolchain
249at the start of a build).
250
251``//BUILD.gn`` is responsible for enumerating each of the Pigweed targets built
252by a project. This is done by instantiating a version of each of the project's
253GN target groups with each Pigweed target's toolchain. For example, in upstream,
254all of Pigweed's GN targets are contained within the ``pigweed_default`` group.
255This group is instantiated multiple times, with different Pigweed target
256toolchains.
257
258These groups include the following:
259
260* ``host`` -- builds ``pigweed_default`` with Clang or GCC, depending on the
261  platform
262* ``host_clang`` -- builds ``pigweed_default`` for the host with Clang
263* ``host_gcc`` -- builds ``pigweed_default`` for the host with GCC
264* ``stm32f429i`` -- builds ``pigweed_default`` for STM32F429i Discovery board
265* ``docs`` -- builds the Pigweed documentation and size reports
266
267Pigweed projects are recommended to follow this pattern, creating a top-level
268group for each of their Pigweed targets that builds a common GN target with the
269appropriate toolchain.
270
271It is important that no dependencies are listed under the default toolchain
272within ``//BUILD.gn``, as it does not configure any build parameters, and
273therefore should not evaluate any other GN files. The pattern that Pigweed uses
274to achieve this is to wrap all dependencies within a condition checking the
275toolchain.
276
277.. code-block::
278
279   group("my_application_images") {
280     deps = []  # Empty in the default toolchain.
281
282     if (current_toolchain != default_toolchain) {
283       # This is only evaluated by Pigweed target toolchains, which configure
284       # all of the required options to build Pigweed code.
285       deps += [ "//images:evt" ]
286     }
287   }
288
289   # The images group is instantiated for each of the project's Pigweed targets.
290   group("my_pigweed_target") {
291     deps = [ ":my_application_images(//toolchains:my_pigweed_target)" ]
292   }
293
294.. warning::
295  Pigweed's default toolchain is never used, so it is set to an empty toolchain
296  which doesn't define any tools. ``//BUILD.gn`` contains conditions which check
297  that the current toolchain is not the default before declaring any GN target
298  dependencies to prevent the default toolchain from evaluating any other BUILD
299  files. All GN targets added to the build must be placed under one of these
300  conditional scopes.
301
302"default" group
303^^^^^^^^^^^^^^^
304The root ``BUILD.gn`` file can define a special group named ``default``. If
305present, Ninja will build this group when invoked without arguments.
306
307.. tip::
308  Defining a ``default`` group makes using ``pw watch`` simple!
309
310Optimization levels
311^^^^^^^^^^^^^^^^^^^
312Pigweed's ``//BUILD.gn`` defines the ``pw_DEFAULT_C_OPTIMIZATION_LEVEL`` build
313arg, which specifies the optimization level to use for the default groups
314(``host``, ``stm32f429i``, etc.). The supported values for
315``pw_DEFAULT_C_OPTIMIZATION_LEVEL`` are:
316
317* ``debug`` -- create debugging-friendly binaries (``-Og``)
318* ``size_optimized`` -- optimize for size (``-Os``)
319* ``speed_optimized`` -- optimized for speed, without increasing code size
320  (``-O2``)
321
322Pigweed defines versions of its groups in ``//BUILD.gn`` for each optimization
323level specified in the ``pw_C_OPTIMIZATION_LEVELS`` list. Rather than relying
324on ``pw_DEFAULT_C_OPTIMIZATION_LEVEL``, you may directly build a group at the
325desired optimization level: ``<group>_<optimization>``. Examples include
326``host_clang_debug``, ``host_gcc_size_optimized``, and
327``stm32f429i_speed_optimized``.
328
329Upstream GN target groups
330^^^^^^^^^^^^^^^^^^^^^^^^^
331In upstream, Pigweed splits its top-level GN targets into a few logical groups,
332which are described below. In order to build a GN target, it *must* be listed in
333one of the groups in this file.
334
335.. important::
336
337  Pigweed's top-level ``BUILD.gn`` file should not be used by downstream
338  projects. Projects that wish to pull all of Pigweed's code into their build
339  may use the ``pw_modules`` and ``pw_module_tests`` variables in
340  ``modules.gni``.
341
342apps
343~~~~
344This group defines the application images built in Pigweed. It lists all of the
345common images built across all Pigweed targets, such as modules' example
346executables. Each Pigweed target can additionally provide its own specific
347images through the ``pw_TARGET_APPLICATIONS`` build arg, which is included by
348this group.
349
350host_tools
351~~~~~~~~~~
352This group defines host-side tooling binaries built for Pigweed.
353
354runtime_sanitizers
355~~~~~~~~~~~~~~~~~~
356This group defines host-side build targets for Clang runtime sanitizers.
357Next runtime sanitizers supported:
358
359* ``asan`` -- `AddressSanitizer`_ is a fast memory error detector.
360* ``msan`` -- `MemorySanitizer`_ is a detector of uninitialized reads.
361* ``ubsan`` -- `UndefinedBehaviorSanitizer`_ is a fast undefined behavior detector.
362* ``ubsan_heuristic`` -- `UndefinedBehaviorSanitizer`_ with the following
363  additional checks enabled:
364
365  * ``integer``: Checks for undefined or suspicious integer behavior.
366  * ``float-divide-by-zero``: Checks for floating point division by zero.
367  * ``implicit-conversion``: Checks for suspicious behavior of implicit conversions.
368  * ``nullability``: Checks for null as function arg, lvalue and return type.
369
370  These additional checks are heuristic and may not correspond to undefined
371  behavior.
372* ``tsan`` -- `ThreadSanitizer`_ is a tool that detects data races.
373
374Results of building this group are ``host_clang_<sanitizer>`` build directories
375with ``pw_module_tests`` per supported sanitizer.
376
377.. _AddressSanitizer: https://clang.llvm.org/docs/AddressSanitizer.html
378.. _MemorySanitizer: https://clang.llvm.org/docs/MemorySanitizer.html
379.. _UndefinedBehaviorSanitizer: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
380.. _ThreadSanitizer: https://clang.llvm.org/docs/ThreadSanitizer.html
381
382coverage
383~~~~~~~~~~
384This group defines host-side build target for Clang source-based code coverage.
385
386pw_modules
387~~~~~~~~~~
388This group lists the main libraries for all of Pigweed's modules.
389
390The modules in the ``pw_modules`` group are listed in the ``pw_modules``
391variable, which is provided by ``modules.gni``.
392
393pw_module_tests
394~~~~~~~~~~~~~~~
395All modules' unit tests are collected here, so that they can all be run at once.
396
397The test groups in ``pw_module_tests`` group are listed in the
398``pw_module_tests`` variable, which is provided by ``modules.gni``.
399
400pigweed_default
401~~~~~~~~~~~~~~~
402This group defines everything built in a Pigweed build invocation by collecting
403the above groups and conditionally depending on them based on the active Pigweed
404target's configuration. Generally, new dependencies should not be added here;
405instead, use one of the groups listed above.
406
407The ``pigweed_default`` group is instantiated for each upstream Pigweed target's
408toolchain.
409
410Pigweed target instantiations
411~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
412These groups wrap ``pigweed_default`` with a specific target toolchain. They are
413named after the Pigweed target, e.g. ``host_clang``, ``stm32f429i``, etc.
414
415Other BUILD files: //\*\*/BUILD.gn
416----------------------------------
417The rest of the ``BUILD.gn`` files in the tree define libraries, configs, and
418build args for each of the modules in a Pigweed project.
419
420Project configuration: //build_overrides/pigweed.gni
421----------------------------------------------------
422Each Pigweed project must contain a Pigweed configuration file at a known
423location in the GN build tree. Currently, this file only contains a single build
424argument, which must be set to the GN build path to the root of the Pigweed
425repository within the project.
426
427Module variables
428----------------
429As Pigweed is intended to be a subcomponent of a larger project, it cannot assume
430where it or its modules is located. Therefore, Pigweed's upstream BUILD.gn files
431do not use absolute paths; instead, variables are defined pointing to each of
432Pigweed's modules, set relative to a project-specific ``dir_pigweed``.
433
434To depend on Pigweed modules from GN code, import Pigweed's overrides file and
435reference these module variables.
436
437.. code-block::
438
439   # This must be imported before .gni files from any other Pigweed modules. To
440   # prevent gn format from reordering this import, it must be separated by a
441   # blank line from other imports.
442
443   import("//build_overrides/pigweed.gni")
444
445GN target type wrappers
446-----------------------
447To facilitate injecting global configuration options, Pigweed defines wrappers
448around builtin GN target types such as ``source_set`` and ``executable``. These
449are defined within ``$dir_pw_build/target_types.gni``.
450
451.. note::
452  To take advantage of Pigweed's flexible target configuration system, use
453  ``pw_*`` target types (e.g. ``pw_source_set``) in your BUILD.gn files instead
454  of GN builtins.
455
456Pigweed targets
457---------------
458To build for a specific hardware platform, builds define Pigweed targets. These
459are essentially GN toolchains which set special arguments telling Pigweed how to
460build. For information on Pigweed's target system, refer to
461:ref:`docs-targets`.
462
463The empty toolchain
464-------------------
465Pigweed's ``BUILDCONFIG.gn`` sets the project's default toolchain to an "empty"
466toolchain which does not specify any compilers or override any build arguments.
467Downstream projects are recommended to do the same, following the steps
468described in :ref:`top-level-build` to configure builds for each of their
469Pigweed targets.
470
471.. admonition:: Why use an empty toolchain?
472
473  To support some of its advanced (and useful!) build features, Pigweed requires
474  the ability to generate new toolchains on the fly. This requires having
475  knowledge of the full configuration of the current toolchain (which is easy if
476  it's all defined within a scope), something which is impractical to achieve
477  using the default toolchain.
478
479  Additionally, there are some cases where GN treats default and non-default
480  toolchains differently. By not using the default toolchain, we avoid having
481  to deal with these inconsistencies.
482
483  It is possible to build Pigweed using only the default toolchain, but it
484  requires a more complicated setup to get everything working and should be
485  avoided unless necessary (for example, when integrating with a large existing
486  GN-based project).
487
488Upstream development examples
489-----------------------------
490If developing for upstream Pigweed, some common build use cases are described
491below.
492
493Building a custom executable/app image
494^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
495
4961. Define your executable GN target using the ``pw_executable`` template.
497
498   .. code-block::
499
500      # //foo/BUILD.gn
501      pw_executable("foo") {
502        sources = [ "main.cc" ]
503        deps = [ ":libfoo" ]
504      }
505
5062. In the root ``BUILD.gn`` file, add the executable's GN target to the ``apps``
507   group.
508
509   .. code-block::
510
511      # //BUILD.gn
512      group("apps") {
513        deps = [
514          # ...
515          "//foo",  # Shorthand for //foo:foo
516        ]
517      }
518
5193. Run the ninja build to compile your executable. The apps group is built by
520   default, so there's no need to provide a target. The executable will be
521   compiled for every supported Pigweed target.
522
523   .. code-block::
524
525      ninja -C out
526
527   Alternatively, build your executable by itself by specifying its path to
528   Ninja. When building a GN target manually, the Pigweed target for which it
529   is built must be specified on the Ninja command line.
530
531   For example, to build for the Pigweed target ``host_gcc_debug``:
532
533   .. code-block::
534
535      ninja -C out host_gcc_debug/obj/foo/bin/foo
536
537   .. note::
538
539     The path passed to Ninja is a filesystem path within the ``out`` directory,
540     rather than a GN path. This path can be found by running ``gn outputs``.
541
5424. Retrieve your compiled binary from the out directory. It is located at the
543   path
544
545   .. code-block::
546
547      out/<pw_target>/obj/<gn_path>/{bin,test}/<executable>
548
549   where ``pw_target`` is the Pigweed target for which the binary was built,
550   ``gn_path`` is the GN path to the BUILD.gn file defining the executable,
551   and ``executable`` is the executable's GN target name (potentially with an
552   extension). Note that the executable is located within a ``bin`` subdirectory
553   in the module (or ``test`` for unit tests defined with ``pw_test``).
554
555   For example, the ``foo`` executable defined above and compiled for the
556   Pigweed target stm32f429i_disc1_debug is found at:
557
558   .. code-block::
559
560      out/stm32f429i_disc1_debug/obj/foo/bin/foo
561
562The CMake build
563===============
564A well-known name in C/C++ development, `CMake`_ is widely used by all kinds of
565projects, including embedded devices. Pigweed's CMake support is provided
566primarily for projects that have an existing CMake build and wish to integrate
567Pigweed modules.
568
569.. _docs-build-system-bazel:
570
571The Bazel build
572===============
573This section describes Pigweed's Bazel build structure, how it is used upstream,
574build conventions, and recommendations for Pigweed-based projects. While
575containing some details about how Bazel works in general, this section is not
576intended to be a guide on how to use Bazel. To learn more about the tool itself,
577refer to the official `Bazel reference`_.
578
579.. _Bazel reference: https://www.bazel.build/
580
581General usage
582-------------
583While described in more detail in the Bazel docs there a few Bazel features that
584are of particular importance when targeting embedded platforms. The most
585commonly used commands used in bazel are;
586
587.. code-block:: sh
588
589   bazelisk build //your:target
590   bazelisk test //your:target
591   bazelisk coverage //your:target
592
593.. note:: Code coverage support is only available on the host for now.
594
595Building
596^^^^^^^^
597When it comes to building/testing your build target for a specific target
598platform (e.g. stm32f429i-discovery) a slight variation is required.
599
600.. code-block:: sh
601
602   bazelisk build //your:target \
603     --platforms=@pigweed//pw_build/platforms:lm3s6965evb
604
605For more information on how to create your own platforms refer to the official
606`Bazel platforms reference`_. You may also find helpful examples of constraints
607and platforms in the ``//pw_build/platforms`` and ``//pw_build/constraints``
608directories.
609
610.. _Bazel platforms reference: https://docs.bazel.build/versions/main/platforms.html
611
612Testing
613^^^^^^^
614Running tests on an embedded target with Bazel is possible although support for
615this is experimental. The easiest way of achieving this at the moment is to use
616Bazel's ``--run_under`` flag. To make this work create a Bazel target
617(``//your_handler``) that:
618
6191. Takes a single argument (the path to the elf) and uploads the elf to your
620   Pigweed target.
6212. Connects with your target using serial or other communication method.
6223. Listens to the communication transport for the keywords ("PASSED", "FAIL")
623   and returns (0, 1) respectively if one of the keywords is intercepted. (This
624   step assumes you are using the pw_unit_test package and it is configured for
625   your target).
626
627Then, run:
628
629   .. code-block:: sh
630
631      bazelisk test //your:test --platforms=//your/platform --run_under=//your_handler
632
633Tag conventions
634~~~~~~~~~~~~~~~
635Pigweed observes the standard Bazel test `tag conventions
636<https://bazel.build/reference/test-encyclopedia#tag-conventions>`_. We also
637use the following additional tags:
638
639*  ``integration``: large, slow integration tests in upstream Pigweed are given
640   the ``integration`` tag.  You can skip running these tests using
641   `--test_tag_filters
642   <https://bazel.build/docs/user-manual#test-tag-filters>`_. For example,
643
644   .. code-block:: sh
645
646      bazelisk test --test_tag_filters=-integration //...
647
648   will run all tests *except* for these integration tests.
649
650*  ``do_not_build``: targets which should not be built in wildcard builds. Any
651   use of this tag should be associated with a TODO pointing to a bug. Prefer
652   ``do_not_build`` to ``manual`` if the target's build graph is correct, and
653   it just fails at the compilation stage.
654
655*  ``do_not_run_test``: test targets which should be built but not executed.
656   Prefer this to ``manual`` or ``do_not_build`` if the code compiles, but the
657   test fails at runtime. Any use of this tag should be associated with a TODO
658   pointing to a bug.
659
660.. _docs-build_system-bazel_coverage:
661
662Code Coverage
663^^^^^^^^^^^^^
664TODO(`b/304833225 <https://issues.pigweed.dev/issues/304833225>`_): Fix code
665coverage when using the (default) hermetic toolchains.
666
667Making use of the code coverage functionality in Bazel is straightforward.
668
6691. Add the following lines to your '.bazelrc'.
670
671   .. code-block:: sh
672
673      coverage --experimental_generate_llvm_lcov
674      coverage --combined_report=lcov
675
6762. Generate a combined lcov coverage report. This will produce a combined lcov
677   coverage report at the path 'bazel-out/_coverage/_coverage_report.dat'. e.g.
678
679   .. code-block:: sh
680
681      bazelisk coverage //pw_log/...
682
6833. View the results using the command line utility 'lcov'.
684
685   .. code-block:: sh
686
687      lcov --list bazel-out/_coverage/_coverage_report.dat
688
689.. _docs-build_system-bazel_link-extra-lib:
690
691Libraries required at linktime
692^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
693Certain low-level libraries (:ref:`module-pw_assert`, :ref:`module-pw_log`) are
694prone to cyclic dependencies. Handling assertions and logging requires using
695other libraries, which themselves may use assertions or logging. To remove
696these cycles, the full implementations of these libraries are placed in special
697*implementation targets* that are not added to their dependencies. Instead,
698every binary with a dependency on these libraries (direct or indirect) must
699link against the implementation targets.
700
701What this means in practice is that any ``cc_binary`` that depends on Pigweed
702libraries should have a dependency on ``//pw_build:default_link_extra_lib``.
703This can be added in a couple ways:
704
705#.  Add ``@pigweed//pw_build:default_link_extra_lib`` directly to the ``deps``
706    of every embedded ``cc_binary`` in your project.
707
708    The con is that you may forget to add the dependency to some targets,
709    and will then encounter puzzling linker errors.
710
711#.  Use `link_extra_lib
712    <https://bazel.build/reference/be/c-cpp#cc_binary.link_extra_lib>`_. Set
713    the ``@bazel_tools//tools/cpp:link_extra_libs`` label flag to point to
714    ``@pigweed//pw_build:default_link_extra_lib``, probably using `bazelrc
715    <https://bazel.build/run/bazelrc>`_. This is only likely to work if you
716    specify the ``--experimental_exclude_starlark_flags_from_exec_config``
717    flag, available in Bazel 8 and newer; see `bazelbuild/bazel#22457
718    <https://github.com/bazelbuild/bazel/issues/22457>`__.
719
720    The con is that these libraries are linked into *all* C++ binaries that are
721    part of your project's build, including ones that have no dependencies on
722    Pigweed.
723
724
725Note that depending on ``@pigweed//pw_build:link_extra_lib`` will
726*unconditionally* include the symbols in the implementation targets in your
727binary, even if the binary does not use them. If this is a concern (e.g., due
728to the binary size increase), depend only on the individual implementation
729targets you actually require.
730
731See :ref:`module-pw_log-circular-deps` and
732:ref:`module-pw_assert-circular-deps` for more information about the specific
733modules that have link-time dependencies.
734
735Configuration
736-------------
737Generally speaking there are three primary concepts that make up Bazel's
738configuration API.
739
7401. Selects
7412. Compatibility lists
7423. Flags/Build settings
743
744Selects
745^^^^^^^
746Selects are useful for specifying different dependencies/source depending on the
747platform that is currently being targeted. For more information on this please
748see the `Bazel selects reference`_. e.g.
749
750.. code-block:: py
751
752   cc_library(
753     name = "some_platform_dependant_library",
754     deps = select({
755       "@platforms//cpu:armv7e-m": [":arm_libs"],
756       "//conditions:default": [":host_libs"],
757     }),
758   )
759
760Compatibility lists
761^^^^^^^^^^^^^^^^^^^
762Compatibility lists allow you to specify which platforms your targets are
763compatible with. Consider an example where you want to specify that a target is
764compatible with only a host os;
765
766.. code-block:: py
767
768   cc_library(
769     name = "some_host_only_lib",
770     srcs = ["host.cc"],
771     target_compatible_with = select({
772       "@platforms//os:windows": [],
773       "@platforms//os:linux": [],
774       "@platforms//os:ios": [],
775       "@platforms//os:macos": [],
776       "//conditions:default": ["@platforms//:incompatible"],
777     }),
778   )
779
780In this case building from or for either Windows/Linux/Mac will be supported, but
781other OS's will fail if this target is explicitly depended on. However if
782building with a wild card for a non-host platform this target will be skipped
783and the build will continue. e.g.
784
785.. code-block:: sh
786
787   bazelisk build //... --platforms=@pigweed//pw_build/platforms:lm3s6965evb
788
789This allows for you to easily create compatibility matricies without adversely
790affecting your ability build your entire repo for a given Pigweed target.
791For more detailed information on how to use the target_compatible_with attribute
792please see `Bazel target_compatible_with reference`_.
793
794.. _docs-build_system-bazel_flags:
795
796Flags/build settings
797^^^^^^^^^^^^^^^^^^^^
798Flags/build settings are particularly useful in scenarios where you may want to
799be able to quickly inject a dependency from the command line but don't
800necessarily want to create an entirely new set of constraints to use with a
801select statement.
802
803.. note::
804  The scope for what is possible with build flags/settings goes well beyond
805  what will be described here. For more detailed information on flags/settings
806  please see `Bazel config reference`_.
807
808A simple example of when it is useful to use a label_flag is when you want to
809swap out a single dependency from the command line. e.g.
810
811.. code-block:: py
812
813   cc_library(
814     name = "some_default_io",
815     srcs = ["default_io.cc"],
816   )
817
818   cc_library(
819     name = "some_other_io",
820     srcs = ["other_io.cc"],
821   )
822
823   label_flag(
824     name = "io",
825     default_build_setting = ":some_default_io",
826   )
827
828   cc_library(
829     name = "some_target_that_needs_io",
830     deps = [":io"],
831   )
832
833From here the label_flag by default redirects to the target ":some_default_io",
834however it is possible to override this from the command line. e.g.
835
836.. code-block:: sh
837
838   bazelisk build //:some_target_that_needs_io --//:io=//:some_other_io
839
840
841
842.. _Bazel selects reference: https://docs.bazel.build/versions/main/configurable-attributes.html#select-and-dependencies
843
844.. _Bazel target_compatible_with reference: https://docs.bazel.build/versions/main/platforms.html#skipping-incompatible-targets
845
846.. _Bazel config reference: https://docs.bazel.build/versions/main/skylark/config.html
847
848.. _docs-build_system-bazel_configuration:
849
850Facades and backends tutorial
851^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
852This section walks you through an example of configuring :ref:`facade
853<module-pw_build-bazel-pw_facade>` backends in a Pigweed Bazel
854project.
855
856Consider a scenario that you are building a flight controller for a spacecraft.
857But you have very little experience with Pigweed and have just landed here.
858First things first, you would set up your WORKSPACE to fetch Pigweed
859repository. Then, add the dependencies that you need from Pigweed's WORKSPACE.
860
861Maybe you want to try using the :ref:`pw_chrono <module-pw_chrono>` module. So
862you create a target in your repository like so:
863
864.. code-block:: python
865
866   # BUILD.bazel
867   cc_library(
868     name = "time_is_relative",
869     srcs = ["relative_time_on_earth.cc"],
870     deps = ["@pigweed//pw_chrono:system_clock"],
871   )
872
873This should work out of the box for any host operating system. E.g., running,
874
875.. code-block:: console
876
877   bazelisk build //:time_is_relative
878
879will produce a working library for your host OS.
880
881Using Pigweed-provided backends
882~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
883But you're probably here because Pigweed offers a set of embedded libraries,
884and might be interested in running your code on some micro-controller/FPGA
885combined with an RTOS.  For now let's assume you are using FreeRTOS and are
886happy to make use of our default ``//pw_chrono`` backend for FreeRTOS. You
887could build your library with:
888
889.. code-block:: console
890
891   bazelisk build \
892     --@pigweed/pw_chrono:system_clock_backend=@pigweed//pw_chrono_freertos:system_clock \
893     //:time_is_relative
894
895Then, ``//pw_chrono:system_clock`` will use the FreeRTOS backend
896``//pw_chrono_freertos:system_clock``.
897
898How does this work?  Here's the relevant part of the dependency tree for your
899target:
900
901.. code-block::
902
903   //:time_is_relative
904    |
905    v
906   @pigweed//pw_chrono:system_clock  -------> @pigweed//pw_chrono:system_clock_backend
907    |                                                    (Injectable)
908    |                                                         |
909    |                                                         v
910    |                                         @pigweed//pw_chrono_freertos:system_clock
911    |                                                   (Actual backend)
912    v                                                         |
913   @pigweed//pw_chrono:system_clock.facade <------------------.
914
915When building ``//:time_is_relative``, Bazel checks the dependencies of
916``@pigweed//pw_chrono:system_clock`` and finds that it depends on
917``@pigweed//pw_chrono:system_clock_backend``, which looks like this:
918
919.. code-block:: python
920
921   # @pigweed//pw_chrono/BUILD.bazel
922
923   label_flag(
924       name = "system_clock_backend",
925       build_setting_default = ":unspecified system_clock_backend",
926   )
927
928This is a  `label_flag
929<https://bazel.build/extending/config#label-typed-build-settings>`_: a
930dependency edge in the build graph that can be overridden by command line
931flags. By setting
932
933.. code-block:: console
934
935   --@pigweed//pw_chrono:system_clock_backend=\
936     @pigweed//pw_chrono_freertos:system_clock
937
938on the command line, you told Bazel to override the default and use the
939FreeRTOS backend.
940
941Defining a custom backend
942~~~~~~~~~~~~~~~~~~~~~~~~~
943Continuing with our scenario, let's say that you have read
944:ref:`docs-module-structure` and now want to implement your own backend for
945``//pw_chrono:system_clock`` using a hardware RTC. In this case you would
946create a new directory ``pw_chrono_my_hardware_rtc``, containing some header
947files and a BUILD file like,
948
949.. code-block:: python
950
951   # //pw_chrono_my_hardware_rtc/BUILD.bazel
952
953   cc_library(
954       name = "system_clock",
955       hdrs = [
956           "public/pw_chrono_stl/system_clock_config.h",
957           "public/pw_chrono_stl/system_clock_inline.h",
958           "public_overrides/pw_chrono_backend/system_clock_config.h",
959           "public_overrides/pw_chrono_backend/system_clock_inline.h",
960       ],
961       includes = [
962           "public",
963           "public_overrides",
964       ],
965       deps = [
966           "@pigweed//pw_chrono:system_clock.facade",
967       ],
968   )
969
970To build your ``//:time_is_relative`` target using this backend, you'd run,
971
972.. code-block:: console
973
974   bazelisk build //:time_is_relative \
975     --@pigweed//pw_chrono:system_clock_backend=//pw_chrono_my_hardware_rtc:system_clock
976
977This modifies the build graph to look something like this:
978
979.. code-block::
980
981   //:time_is_relative
982    |
983    v
984   @pigweed//pw_chrono:system_clock  -------> @pigweed//pw_chrono:system_clock_backend
985    |                                                    (Injectable)
986    |                                                         |
987    |                                                         v
988    |                                         //pw_chrono_my_hardware_rtc:system_clock
989    |                                                   (Actual backend)
990    v                                                         |
991   @pigweed//pw_chrono:system_clock.facade <------------------.
992
993Associating backends with platforms through bazelrc
994~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
995As your project grows, you will want to select backends for an increasing
996number of facades. The right backend to choose will depend on the target
997platform (host vs embedded, with potentially multiple target embedded
998platforms). Managing this directly through command-line flags is generally an
999anti-pattern. Instead, group these flags into configs in your
1000`bazelrc <https://bazel.build/run/bazelrc>`_. Eventually, your bazelrc may look
1001something like this:
1002
1003.. code-block:: sh
1004
1005   # The Cortex M7 config
1006   build:m7 --@pigweed//pw_chrono:system_clock_backend=//pw_chrono_my_hardware_rtc:system_clock
1007   build:m7 --@pigweed//pw_sys_io:backend=//cortex-m7:sys_io
1008
1009   # The Cortex M4 config
1010   build:m4 --@pigweed//pw_chrono:system_clock_backend=//pw_chrono_my_hardware_rtc:system_clock
1011   build:m4 --@pigweed//pw_sys_io:backend=//cortex-m4:sys_io
1012   build:m4 --@pigweed//pw_log:backend=@pigweed//pw_log_string
1013   build:m4 --@pigweed//pw_log_string:handler_backend=@pigweed//pw_system:log_backend
1014
1015Then, to build your library for a particular configuration, on the command line
1016you just specify the ``--config`` on the command line:
1017
1018.. code-block:: console
1019
1020   bazelisk build --config=m4 //:time_is_relative
1021