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