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