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