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