• 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
166GN
167--
168A perhaps unfamiliar name, `GN (Generate Ninja)`_ is a meta-build system that
169outputs `Ninja`_ build files, originally designed for use in Chromium. Pigweed
170first experimented with GN after hearing about it from another team, and we
171quickly came to appreciate its speed and simplicity. GN has become Pigweed's
172primary build system; it is used for all upstream development and strongly
173recommended for Pigweed-based projects where possible.
174
175.. _CMake: https://cmake.org/
176.. _Bazel: https://bazel.build/
177.. _GN (Generate Ninja): https://gn.googlesource.com/gn
178.. _Ninja: https://ninja-build.org/
179
180The GN build
181============
182This section describes Pigweed's GN build structure, how it is used upstream,
183build conventions, and recommendations for Pigweed-based projects. While
184containing some details about how GN works in general, this section is not
185intended to be a guide on how to use GN. To learn more about the tool itself,
186refer to the official `GN reference`_.
187
188.. _GN reference: https://gn.googlesource.com/gn/+/HEAD/docs/reference.md
189
190Entrypoint: .gn
191---------------
192The entrypoint to a GN build is the ``.gn`` file, which defines a project's root
193directory (henceforth ``//``).
194
195``.gn`` must point to the location of a ``BUILDCONFIG.gn`` file for the project.
196In Pigweed upstream, this is its only purpose.
197
198Downstream projects may additionally use ``.gn`` to set global overrides for
199Pigweed's build arguments, which apply across all Pigweed targets. For example,
200a project could configure the protobuf libraries that it uses. This is done by
201defining a ``default_args`` scope containing the overrides.
202
203.. code::
204
205  # The location of the BUILDCONFIG file.
206  buildconfig = "//BUILDCONFIG.gn"
207
208  # Build arguments set across all Pigweed targets.
209  default_args = {
210    dir_pw_third_party_nanopb = "//third_party/nanopb-0.4.2"
211  }
212
213Configuration: BUILDCONFIG.gn
214-----------------------------
215The file ``BUILDCONFIG.gn`` configures the GN build by defining any desired
216global variables/options. It can be located anywhere in the build tree, but is
217conventionally placed at the root. ``.gn`` points GN to this file.
218
219``BUILDCONFIG.gn`` is evaluated before any other GN files, and variables defined
220within it are placed into GN's global scope, becoming available in every file
221without requiring imports.
222
223The options configured in this file differ from those in ``.gn`` in two ways:
224
2251. ``BUILDCONFIG.gn`` is evaluated for every GN toolchain (and Pigweed target),
226   whereas ``.gn`` is only evaluated once. This allows ``BUILDCONFIG.gn`` to set
227   different options for each Pigweed target.
2282. In ``.gn``, only GN build arguments can be overridden. ``BUILDCONFIG.gn``
229   allows defining arbitrary variables.
230
231Generally, it is preferable to expose configuration options through build args
232instead of globals in ``BUILDCONFIG.gn`` (something Pigweed's build previously
233did), as they are more flexible, greppable, and easier to manage. However, it
234may make sense to define project-specific constants in ``BUILDCONFIG.gn``.
235
236Pigweed's upstream ``BUILDCONFIG.gn`` does not define any variables; it just
237sets Pigweed's default toolchain, which GN requires.
238
239.. _top-level-build:
240
241Top-level GN targets: //BUILD.gn
242--------------------------------
243The root ``BUILD.gn`` file defines all of the libraries, images, tests, and
244binaries built by a Pigweed project. This file is evaluated immediately after
245``BUILDCONFIG.gn``, with the active toolchain (which is the default toolchain
246at the start of a build).
247
248``//BUILD.gn`` is responsible for enumerating each of the Pigweed targets built
249by a project. This is done by instantiating a version of each of the project's
250GN target groups with each Pigweed target's toolchain. For example, in upstream,
251all of Pigweed's GN targets are contained within the ``pigweed_default`` group.
252This group is instantiated multiple times, with different Pigweed target
253toolchains.
254
255These groups include the following:
256
257* ``host`` -- builds ``pigweed_default`` with Clang or GCC, depending on the
258  platform
259* ``host_clang`` -- builds ``pigweed_default`` for the host with Clang
260* ``host_gcc`` -- builds ``pigweed_default`` for the host with GCC
261* ``stm32f429i`` -- builds ``pigweed_default`` for STM32F429i Discovery board
262* ``docs`` -- builds the Pigweed documentation and size reports
263
264Pigweed projects are recommended to follow this pattern, creating a top-level
265group for each of their Pigweed targets that builds a common GN target with the
266appropriate toolchain.
267
268It is important that no dependencies are listed under the default toolchain
269within ``//BUILD.gn``, as it does not configure any build parameters, and
270therefore should not evaluate any other GN files. The pattern that Pigweed uses
271to achieve this is to wrap all dependencies within a condition checking the
272toolchain.
273
274.. code::
275
276  group("my_application_images") {
277    deps = []  # Empty in the default toolchain.
278
279    if (current_toolchain != default_toolchain) {
280      # This is only evaluated by Pigweed target toolchains, which configure
281      # all of the required options to build Pigweed code.
282      deps += [ "//images:evt" ]
283    }
284  }
285
286  # The images group is instantiated for each of the project's Pigweed targets.
287  group("my_pigweed_target") {
288    deps = [ ":my_application_images(//toolchains:my_pigweed_target)" ]
289  }
290
291.. warning::
292  Pigweed's default toolchain is never used, so it is set to an empty toolchain
293  which doesn't define any tools. ``//BUILD.gn`` contains conditions which check
294  that the current toolchain is not the default before declaring any GN target
295  dependencies to prevent the default toolchain from evaluating any other BUILD
296  files. All GN targets added to the build must be placed under one of these
297  conditional scopes.
298
299"default" group
300^^^^^^^^^^^^^^^
301The root ``BUILD.gn`` file can define a special group named ``default``. If
302present, Ninja will build this group when invoked without arguments.
303
304.. tip::
305  Defining a ``default`` group makes using ``pw watch`` simple!
306
307Optimization levels
308^^^^^^^^^^^^^^^^^^^
309Pigweed's ``//BUILD.gn`` defines the ``pw_default_optimization_level`` build
310arg, which specifies the optimization level to use for the default groups
311(``host``, ``stm32f429i``, etc.). The supported values for
312``pw_default_optimization_level`` are:
313
314* ``debug`` -- create debugging-friendly binaries (``-Og``)
315* ``size_optimized`` -- optimize for size (``-Os``)
316* ``speed_optimized`` -- optimized for speed, without increasing code size
317  (``-O2``)
318
319Pigweed defines versions of its groups in ``//BUILD.gn`` for each optimization
320level. Rather than relying on ``pw_default_optimization_level``, you may
321directly build a group at the desired optimization level:
322``<group>_<optimization>``. Examples include ``host_clang_debug``,
323``host_gcc_size_optimized``, and ``stm32f429i_speed_optimized``.
324
325Upstream GN target groups
326^^^^^^^^^^^^^^^^^^^^^^^^^
327In upstream, Pigweed splits its top-level GN targets into a few logical groups,
328which are described below. In order to build a GN target, it *must* be listed in
329one of the groups in this file.
330
331.. important::
332
333  Pigweed's top-level ``BUILD.gn`` file should not be used by downstream
334  projects. Projects that wish to pull all of Pigweed's code into their build
335  may use the ``pw_modules`` and ``pw_module_tests`` variables in
336  ``modules.gni``.
337
338apps
339~~~~
340This group defines the application images built in Pigweed. It lists all of the
341common images built across all Pigweed targets, such as modules' example
342executables. Each Pigweed target can additionally provide its own specific
343images through the ``pw_TARGET_APPLICATIONS`` build arg, which is included by
344this group.
345
346host_tools
347~~~~~~~~~~
348This group defines host-side tooling binaries built for Pigweed.
349
350runtime_sanitizers
351~~~~~~~~~~~~~~~~~~
352This group defines host-side build targets for Clang runtime sanitizers.
353Next runtime sanitizers supported:
354
355* ``asan`` -- `AddressSanitizer`_ is a fast memory error detector.
356* ``msan`` -- `MemorySanitizer`_ is a detector of uninitialized reads.
357* ``ubsan`` -- `UndefinedBehaviorSanitizer`_ is a fast undefined behavior detector.
358* ``ubsan_heuristic`` -- `UndefinedBehaviorSanitizer`_ with the following
359  additional checks enabled:
360
361   * ``integer``: Checks for undefined or suspicious integer behavior.
362   * ``float-divide-by-zero``: Checks for floating point division by zero.
363   * ``implicit-conversion"``: Checks for suspicious behavior of implicit conversions.
364   * ``nullability``: Checks for null as function arg, lvalue and return type.
365
366  These additional checks are heuristic and may not correspond to undefined
367  behavior.
368* ``tsan`` -- `ThreadSanitizer`_ is a tool that detects data races.
369
370Results of building this group are ``host_clang_<sanitizer>`` build directories
371with ``pw_module_tests`` per supported sanitizer.
372
373.. _AddressSanitizer: https://clang.llvm.org/docs/AddressSanitizer.html
374.. _MemorySanitizer: https://clang.llvm.org/docs/MemorySanitizer.html
375.. _UndefinedBehaviorSanitizer: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
376.. _ThreadSanitizer: https://clang.llvm.org/docs/ThreadSanitizer.html
377
378pw_modules
379~~~~~~~~~~
380This group lists the main libraries for all of Pigweed's modules.
381
382The modules in the ``pw_modules`` group are listed in the ``pw_modules``
383variable, which is provided by ``modules.gni``.
384
385pw_module_tests
386~~~~~~~~~~~~~~~
387All modules' unit tests are collected here, so that they can all be run at once.
388
389The test groups in ``pw_module_tests`` group are listed in the
390``pw_module_tests`` variable, which is provided by ``modules.gni``.
391
392pigweed_default
393~~~~~~~~~~~~~~~
394This group defines everything built in a Pigweed build invocation by collecting
395the above groups and conditionally depending on them based on the active Pigweed
396target's configuration. Generally, new dependencies should not be added here;
397instead, use one of the groups listed above.
398
399The ``pigweed_default`` group is instantiated for each upstream Pigweed target's
400toolchain.
401
402Pigweed target instantiations
403~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
404These groups wrap ``pigweed_default`` with a specific target toolchain. They are
405named after the Pigweed target, e.g. ``host_clang``, ``stm32f429i``, etc.
406
407Other BUILD files: //\*\*/BUILD.gn
408----------------------------------
409The rest of the ``BUILD.gn`` files in the tree define libraries, configs, and
410build args for each of the modules in a Pigweed project.
411
412Project configuration: //build_overrides/pigweed.gni
413----------------------------------------------------
414Each Pigweed project must contain a Pigweed configuration file at a known
415location in the GN build tree. Currently, this file only contains a single build
416argument, which must be set to the GN build path to the root of the Pigweed
417repository within the project.
418
419Module variables
420----------------
421As Pigweed is intended to be a subcomponent of a larger project, it cannot assume
422where it or its modules is located. Therefore, Pigweed's upstream BUILD.gn files
423do not use absolute paths; instead, variables are defined pointing to each of
424Pigweed's modules, set relative to a project-specific ``dir_pigweed``.
425
426To depend on Pigweed modules from GN code, import Pigweed's overrides file and
427reference these module variables.
428
429.. code::
430
431  # This must be imported before .gni files from any other Pigweed modules. To
432  # prevent gn format from reordering this import, it must be separated by a
433  # blank line from other imports.
434
435  import("//build_overrides/pigweed.gni")
436
437GN target type wrappers
438-----------------------
439To facilitate injecting global configuration options, Pigweed defines wrappers
440around builtin GN target types such as ``source_set`` and ``executable``. These
441are defined within ``$dir_pw_build/target_types.gni``.
442
443.. note::
444  To take advantage of Pigweed's flexible target configuration system, use
445  ``pw_*`` target types (e.g. ``pw_source_set``) in your BUILD.gn files instead
446  of GN builtins.
447
448Pigweed targets
449---------------
450To build for a specific hardware platform, builds define Pigweed targets. These
451are essentially GN toolchains which set special arguments telling Pigweed how to
452build. For information on Pigweed's target system, refer to
453:ref:`docs-targets`.
454
455The empty toolchain
456-------------------
457Pigweed's ``BUILDCONFIG.gn`` sets the project's default toolchain to a "empty"
458toolchain which does not specify any compilers or override any build arguments.
459Downstream projects are recommended to do the same, following the steps
460described in :ref:`top-level-build` to configure builds for each of their
461Pigweed targets.
462
463.. admonition:: Why use an empty toolchain?
464
465  To support some of its advanced (and useful!) build features, Pigweed requires
466  the ability to generate new toolchains on the fly. This requires having
467  knowledge of the full configuration of the current toolchain (which is easy if
468  it's all defined within a scope), something which is impractical to achieve
469  using the default toolchain.
470
471  Additionally, there are some cases where GN treats default and non-default
472  toolchains differently. By not using the default toolchain, we avoid having
473  to deal with these inconsistencies.
474
475  It is possible to build Pigweed using only the default toolchain, but it
476  requires a more complicated setup to get everything working and should be
477  avoided unless necessary (for example, when integrating with a large existing
478  GN-based project).
479
480Upstream development examples
481-----------------------------
482If developing for upstream Pigweed, some common build use cases are described
483below.
484
485Building a custom executable/app image
486^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
487
4881. Define your executable GN target using the ``pw_executable`` template.
489
490   .. code::
491
492     # //foo/BUILD.gn
493     pw_executable("foo") {
494       sources = [ "main.cc" ]
495       deps = [ ":libfoo" ]
496     }
497
4982. In the root ``BUILD.gn`` file, add the executable's GN target to the ``apps``
499   group.
500
501   .. code::
502
503     # //BUILD.gn
504     group("apps") {
505       deps = [
506         # ...
507         "//foo",  # Shorthand for //foo:foo
508       ]
509     }
510
5113. Run the ninja build to compile your executable. The apps group is built by
512   default, so there's no need to provide a target. The executable will be
513   compiled for every supported Pigweed target.
514
515   .. code::
516
517     ninja -C out
518
519   Alternatively, build your executable by itself by specifying its path to
520   Ninja. When building a GN target manually, the Pigweed target for which it
521   is built must be specified on the Ninja command line.
522
523   For example, to build for the Pigweed target ``host_gcc_debug``:
524
525   .. code::
526
527     ninja -C out host_gcc_debug/obj/foo/bin/foo
528
529   .. note::
530
531     The path passed to Ninja is a filesystem path within the ``out`` directory,
532     rather than a GN path. This path can be found by running ``gn outputs``.
533
5344. Retrieve your compiled binary from the out directory. It is located at the
535   path
536
537   .. code::
538
539     out/<pw_target>/obj/<gn_path>/{bin,test}/<executable>
540
541   where ``pw_target`` is the Pigweed target for which the binary was built,
542   ``gn_path`` is the GN path to the BUILD.gn file defining the executable,
543   and ``executable`` is the executable's GN target name (potentially with an
544   extension). Note that the executable is located within a ``bin`` subdirectory
545   in the module (or ``test`` for unit tests defined with ``pw_test``).
546
547   For example, the ``foo`` executable defined above and compiled for the
548   Pigweed target stm32f429i_disc1_debug is found at:
549
550   .. code::
551
552     out/stm32f429i_disc1_debug/obj/foo/bin/foo
553
554CMake
555-----
556A well-known name in C/C++ development, `CMake`_ is widely used by all kinds of
557projects, including embedded devices. Pigweed's CMake support is provided
558primarily for projects that have an existing CMake build and wish to integrate
559Pigweed modules.
560
561Bazel
562-----
563The open source version of Google's internal build system. `Bazel`_ has been
564growing in popularity within the open source world, as well as being adopted by
565various proprietary projects. Its modular structure makes it a great fit for
566à la carte usage.
567
568.. note::
569  Bazel support is experimental and only for the brave for now. If you are
570  looking for stable set of build API's please use GN.
571
572The Bazel build
573===============
574This section describes Pigweed's Bazel build structure, how it is used upstream,
575build conventions, and recommendations for Pigweed-based projects. While
576containing some details about how Bazel works in general, this section is not
577intended to be a guide on how to use Bazel. To learn more about the tool itself,
578refer to the official `Bazel reference`_.
579
580.. _Bazel reference: https://www.bazel.build/
581
582General usage
583-------------
584While described in more detail in the Bazel docs there a few Bazel features that
585are of particular importance when targeting embedded platforms. The most
586commonly used commands used in bazel are;
587
588.. code:: sh
589
590  bazel build //your:target
591  bazel test //your:target
592  bazel coverage //your:target
593
594.. note:: Code coverage support is only available on the host for now.
595
596Building
597^^^^^^^^
598When it comes to building/testing your Bazel target for a specific Pigweed
599target (e.g. stm32f429i-discovery) a slight variation is required.
600
601.. code:: sh
602
603  bazel build //your:target \
604    --platforms=@pigweed//pw_build/platforms:stm32f429i-disc1
605
606For more information on how to create your own platforms refer to the official
607`Bazel platforms reference`_. You may also find helpful examples of constraints
608and platforms in the '//pw_build/platforms' and '//pw_build/constraints'
609directories.
610
611.. _Bazel platforms reference: https://docs.bazel.build/versions/main/platforms.html
612
613Testing
614^^^^^^^
615Running tests on an embedded target with Bazel is possible although support for
616this is experimental. The easiest way of achieving this at the moment is to use
617Bazel's '--run_under' flag. To make this work create a Bazel target
618('//your_handler') that;
619
6201. Takes a single argument (the path to the elf) and uploads the elf to your
621   Pigweed target.
6222. Connects with your target using serial or other communication method.
6233. Listens to the communication transport for the keywords ("PASSED", "FAIL")
624   and returns (0, 1) respectively if one of the keywords is intercepted. (This
625   step assumes you are using the pw_unit_test package and it is configured for
626   your target).
6274. Run;
628
629   .. code:: sh
630
631    bazel test //your:test --platforms=//your/platform --run_under=//your_handler
632
633Code Coverage
634^^^^^^^^^^^^^
635Making use of the code coverage functionality in Bazel is straightforward.
636
6371. Add the following lines to your '.bazelrc'.
638
639  .. code:: sh
640
641    coverage --experimental_generate_llvm_lcov
642    coverage --combined_report=lcov
643
6442. Generate a combined lcov coverage report. This will produce a combined lcov
645   coverage report at the path 'bazel-out/_coverage/_coverage_report.dat'. e.g.
646
647  .. code:: sh
648
649    bazel coverage //pw_log/...
650
6513. View the results using the command line utility 'lcov'.
652
653  .. code:: sh
654
655    lcov --list bazel-out/_coverage/_coverage_report.dat
656
657Configuration
658-------------
659Generally speaking there are three primary concepts that make up Bazel's
660configuration API.
661
6621. Selects
6632. Compatibility lists
6643. Flags/Build settings
665
666Selects
667^^^^^^^
668Selects are useful for specifying different dependencies/source depending on the
669platform that is currently being targeted. For more information on this please
670see the `Bazel selects reference`_. e.g.
671
672.. code:: py
673
674  pw_cc_library(
675    name = "some_platform_dependant_library",
676    deps = select({
677      "@platforms//cpu:armv7e-m": [":arm_libs"],
678      "//conditions:default": [":host_libs"],
679    }),
680  )
681
682Compatibility lists
683^^^^^^^^^^^^^^^^^^^
684Compatibility lists allow you to specify which platforms your targets are
685compatible with. Consider an example where you want to specify that a target is
686compatible with only a host os;
687
688.. code:: py
689
690  pw_cc_library(
691    name = "some_host_only_lib",
692    srcs = ["host.cc"],
693    target_compatible_with = select({
694      "@platforms//os:windows": [],
695      "@platforms//os:linux": [],
696      "@platforms//os:macos": [],
697      "//conditions:default": ["@platforms//:incompatible"],
698    }),
699  )
700
701In this case building from or for either Windows/Linux/Mac will be supported, but
702other OS's will fail if this target is explicitly depended on. However if
703building with a wild card for a non-host platform this target will be skipped
704and the build will continue. e.g.
705
706.. code:: sh
707
708  bazel build //... --platforms=@pigweed//pw_build/platforms:cortex_m0
709
710This allows for you to easily create compatibility matricies without adversely
711affecting your ability build your entire repo for a given Pigweed target.
712For more detailed information on how to use the target_compatible_with attribute
713please see `Bazel target_compatible_with reference`_.
714
715Flags/build settings
716^^^^^^^^^^^^^^^^^^^^
717Flags/build settings are particularly useful in scenarios where you may want to
718be able to quickly inject a dependency from the command line but don't
719necessarily want to create an entirely new set of constraints to use with a
720select statement.
721
722.. note::
723  The scope for what is possible with build flags/settings goes well beyond
724  what will be described here. For more detailed information on flags/settings
725  please see `Bazel config reference`_.
726
727A simple example of when it is useful to use a label_flag is when you want to
728swap out a single dependency from the command line. e.g.
729
730.. code:: py
731
732  pw_cc_library(
733    name = "some_default_io",
734    srcs = ["default_io.cc"],
735  )
736
737  pw_cc_library(
738    name = "some_other_io",
739    srcs = ["other_io.cc"],
740  )
741
742  label_flag(
743    name = "io",
744    default_build_setting = ":some_default_io",
745  )
746
747  pw_cc_library(
748    name = "some_target_that_needs_io",
749    deps = [":io"],
750  )
751
752From here the label_flag by default redirects to the target ":some_default_io",
753however it is possible to override this from the command line. e.g.
754
755.. code:: sh
756
757  bazel build //:some_target_that_needs_io --//:io=//:some_other_io
758
759
760
761.. _Bazel selects reference: https://docs.bazel.build/versions/main/configurable-attributes.html#select-and-dependencies
762
763.. _Bazel target_compatible_with reference: https://docs.bazel.build/versions/main/platforms.html#skipping-incompatible-targets
764
765.. _Bazel config reference: https://docs.bazel.build/versions/main/skylark/config.html
766
767
768Pigweeds configuration
769^^^^^^^^^^^^^^^^^^^^^^
770Pigweeds Bazel configuration API is designed to be distributed across the
771Pigweed repository and/or your downstream repository. If you are coming from
772GN's centralized configuration API it might be useful to think about
773Pigweed+Bazel's configuration as the transpose of GN's configuration. The
774configuration interface that is supported by Pigweed is designed to start simple
775and then grow with your project.
776
777.. note::
778  There are plans to extend the configuration API for Bazel. However,
779  currently the only configurations that are available under the Bazel+Pigweed
780  configuration API is the ability to switch facade backends. For more
781  information on what this is please see the
782  :ref:`docs-module-structure-facades` section of :ref:`docs-module-structure`.
783
784Consider a scenario that you are building a flight controller for a
785spacecraft. But have very little experience with Pigweed and you have just
786landed here. First things first you would;
787
7881. Set up your WORKSPACE to fetch the Pigweeds repository. Then add the
789   dependencies that you need from Pigweeds WORKSPACE.
790
7912. Add a pigweed_config rule to your WORKSPACE, using Pigweed's default
792   configuration.
793
794  .. code:: py
795
796    # WORKSPACE ...
797    load("//pw_build:target_config.bzl", "pigweed_config")
798
799    # Configure Pigweeds backend.
800    pigweed_config(
801        name = "pigweed_config",
802        build_file = "@pigweed//targets:default_config.BUILD",
803    )
804
805.. note::
806  We are aware, that the experience of setting up your WORKSPACE file to work
807  with pigweed is less than ideal. This API is under construction, but we are
808  working on this!
809
810..
811  TODO: Add in a better way to pull down WORKSPACE dependencies in Bazel then
812  add docs in here.
813
814Now to explain what is going on here. Housed under the "pigweed_config" remote
815repository is a set of configuration flags. These can be used to inject
816dependencies into facades to override the default backend.
817
818Continuing on with our scenario, consider that you maybe want to try using the
819'//pw_chrono' module. So you create a target in your repository like so;
820
821.. code::
822
823  # BUILD
824  pw_cc_library(
825    name = "time_is_relative",
826    srcs = ["relative_time_on_earth.cc"],
827    deps = ["@pigweed//pw_chrono"],
828  )
829
830Now this should work out of the box for any host operating system. e.g. Running;
831
832.. code::
833
834  bazel build //:time_is_relative
835
836will produce a working library. But as your probably here because Pigweed offers
837a set of embedded libraries you might be interested in running your code on some
838random micro-controller/FPGA combined with an RTOS. For now let's assume that by
839some coincidence you are using FreeRTOS and are happy to make use
840of our default '//pw_chrono' backend for FreeRTOS. You could build the following
841with;
842
843.. code:: sh
844
845  bazel build //:time_is_relative \
846  --platforms=@pigweed//pw_build/platforms:freertos
847
848There is a fair bit to unpack here in terms of how our configuration system
849is determining which dependencies to choose for your build. The dependency
850tree (that is important for configuration) in a project such as this would
851look like.
852
853.. code::
854
855  @pigweed//pw_chrono:pw_chrono_facade <-----------.
856   ^                                               |
857   |                            @pigweed//pw_chrono_freertos:system_clock
858   |                            (Actual backend)
859   |                                               ^
860   |                                               |
861   |                            @pigweed//pw_chrono:system_clock_backend_multiplexer
862   |                            Select backend based on OS:
863   |                            [FreeRTOS (X), Embos ( ), STL ( ), Threadx ( )]
864   |                                               ^
865   |                                               |
866  @pigweed//pw_chrono  -------> @pigweed_config//:pw_chrono_system_clock_backend
867   ^                            (Injectable)
868   |
869  //:time_is_relative
870
871So when evaluating this setup Bazel checks the dependencies for '//pw_chrono'
872and finds that it depends on "@pigweed_config//:pw_chrono_system_clock_backend" which looks
873like this;
874
875.. code:: py
876
877  # pw_chrono config.
878  label_flag(
879      name = "pw_chrono_system_clock_backend",
880      build_setting_default = "@pigweed//pw_chrono:system_clock_backend_multiplexer",
881  )
882
883Looking at the 'build_setting_default' we can see that by default it depends
884back on the target "@pigweed//pw_chrono:system_clock_backend_multiplexer". If
885you only had one backend you could actually just change the
886'build_setting_default' to point directly to your backend. However because we
887have four different backends we have to use the select semantics to choose the
888right one. In this case it looks like;
889
890.. code:: py
891
892  pw_cc_library(
893    name = "system_clock_backend_multiplexer",
894    visibility = ["@pigweed_config//:__pkg__"],
895    deps = select({
896        "@pigweed//pw_build/constraints/rtos:freertos":
897            ["//pw_chrono_freertos:system_clock"],
898        "@pigweed//pw_build/constraints/rtos:embos":
899            ["//pw_chrono_embos:system_clock"],
900        "@pigweed//pw_build/constraints/rtos:threadx":
901            ["//pw_chrono_threadx:system_clock"],
902        "//conditions:default": ["//pw_chrono_stl:system_clock"],
903    }),
904  )
905
906Intuitively you can see that the first option was selected, which terminates
907the configuration chain.
908
909Continuing on with our scenario let's say that you have read
910:ref:`docs-module-structure` and now want to implement your own backend for
911'//pw_chrono' using a hardware RTC. In this case you would create a new
912directory 'pw_chrono_my_hardware_rtc'. To ensure that your new backend compiles
913with the facade an easy and temporary way to override the dependency tree is
914to override the label flag in '@pigweed_config'. For example;
915
916.. code:: sh
917
918  bazel build //:time_is_relative \
919    --@pigweed_config//pw_chrono_system_clock_backend=//pw_chrono_my_hardware_rtc:system_clock
920
921This temporarily modifies the build graph to look something like this;
922
923.. code::
924
925  @pigweed//pw_chrono:pw_chrono_facade <-----.
926   ^                                         |
927   |                      @your_workspace//pw_chrono_my_hardware_rtc:system_clock
928   |                      (Actual backend)
929   |                                         ^
930   |                                         |
931  @pigweed//pw_chrono  -> @pigweed_config//:pw_chrono_system_clock_backend
932   ^                      (Injectable)
933   |
934  //:time_is_relative
935
936Now while this is a nice temporary change, but you might find yourself in need
937of a more permanent configuration. Particularly if you want to override multiple
938different backends. In other words if you had several backends to override, that
939would translate to several different command line flags (one for each override).
940This problem further compounds as you have multiple Pigweed targets all
941requiring different combinations of different backends as you can't even reuse
942your command line entries. Instead you would have to memorize the correct
943combination of backends for each of your targets.
944
945So continuing on with our scenario, let's say we add a backup micro-controller,
946to our spacecraft. But this backup computer doesn't have a hardware RTC. We
947still want to share the bulk of the code between the two computers but now we
948need two separate implementations for our pw_chrono facade. Let's say we choose
949to keep the primary flight computer using the hardware RTC and switch the backup
950computer over to use Pigweeds default FreeRTOS backend. In this case we might,
951want to do something similar to
952'@pigweed//pw_chrono:system_clock_backend_multiplexer' and create selectable
953dependencies for the two different computers. Now because there are no default
954constraint_setting's that meet our requirements we are going to have to;
955
9561. Create a constraint_setting and a set of constraint_value's for the flight
957   computer. For example;
958
959  .. code:: py
960
961    # //platforms/flight_computer/BUILD
962    constraint_setting(
963      name = "flight_computer",
964    )
965
966    constraint_value(
967      name = "primary",
968      constraint_setting = ":flight_computer",
969    )
970
971    constraint_value(
972      name = "backup",
973      constraint_setting = ":flight_computer",
974    )
975
9762. Create a set of platforms that can be used to switch constraint_value's.
977   For example;
978
979  .. code:: py
980
981    # //platforms/BUILD
982    platform(
983      name = "primary_computer",
984      constraint_values = ["//platforms/flight_computer:primary"],
985    )
986
987    platform(
988      name = "backup_computer",
989      constraint_values = ["//platforms/flight_computer:backup"],
990    )
991
9923. Create a target multiplexer that will select the right backend depending on
993   which computer you are using. For example;
994
995  .. code:: py
996
997    # //pw_chrono/BUILD
998    load("//pw_build:pigweed.bzl", "pw_cc_library")
999
1000    pw_cc_library(
1001      name = "system_clock_backend_multiplexer",
1002      deps = select({
1003        "//platforms/flight_computer:primary": [
1004          "//pw_chrono_my_hardware_rtc:system_clock",
1005        ],
1006        "//platforms/flight_computer:backup": [
1007          "@pigweed//pw_chrono_freertos:system_clock",
1008        ],
1009        "//conditions:default": [
1010          "@pigweed//pw_chrono_stl:system_clock",
1011        ],
1012      }),
1013    )
1014
10154. Copy and paste across the target/default_config.BUILD across from the
1016   Pigweed repository and modifying the build_setting_default for the target
1017   'pw_chrono_system_clock_backend' to point to your new system_clock_backend_multiplexer
1018   target. For example;
1019
1020   This;
1021
1022  .. code:: py
1023
1024    # @pigweed//target:default_config.BUILD
1025    label_flag(
1026        name = "pw_chrono_system_clock_backend",
1027        build_setting_default = "@pigweed//pw_chrono:system_clock_backend_multiplexer",
1028    )
1029
1030  Becomes this;
1031
1032  .. code:: py
1033
1034    # @your_workspace//target:your_config.BUILD
1035    label_flag(
1036      name = "pw_chrono_system_clock_backend",
1037      build_setting_default =
1038        "@your_workspace//pw_chrono:system_clock_backend_multiplexer",
1039    )
1040
10415. Switch your workspace 'pigweed_config' rule over to use your custom config.
1042
1043  .. code:: py
1044
1045    # WORKSPACE
1046    pigweed_config(
1047      name = "pigweed_config",
1048      build_file = "//target/your_config.BUILD",
1049    )
1050
1051Building your target now will result in slightly different build graph. For
1052example, running;
1053
1054.. code:: sh
1055
1056  bazel build //:time_is_relative --platforms=//platforms:primary_computer
1057
1058Will result in a build graph that looks like;
1059
1060.. code::
1061
1062  @pigweed//pw_chrono:pw_chrono_facade <---.
1063   ^                                        |
1064   |                     @your_workspace//pw_chrono_my_hardware_rtc:system_clock
1065   |                     (Actual backend)
1066   |                                        ^
1067   |                                        |
1068   |                     @your_workspace//pw_chrono:system_clock_backend_multiplexer
1069   |                     Select backend based on OS:
1070   |                     [Primary (X), Backup ( ), Host only default ( )]
1071   |                                        ^
1072   |                                        |
1073  @pigweed//pw_chrono -> @pigweed_config//:pw_chrono_system_clock_backend
1074   ^                     (Injectable)
1075   |
1076  //:time_is_relative
1077