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 157CMake 158----- 159A well-known name in C/C++ development, `CMake`_ is widely used by all kinds of 160projects, including embedded devices. Pigweed's CMake support is provided 161primarily for projects that have an existing CMake build and wish to integrate 162Pigweed modules. 163 164Bazel 165----- 166The open source version of Google's internal build system. `Bazel`_ has been 167growing in popularity within the open source world, as well as being adopted by 168various proprietary projects. Its modular structure makes it a great fit for 169à la carte usage. 170 171GN 172-- 173A perhaps unfamiliar name, `GN (Generate Ninja)`_ is a meta-build system that 174outputs `Ninja`_ build files, originally designed for use in Chromium. Pigweed 175first experimented with GN after hearing about it from another team, and we 176quickly came to appreciate its speed and simplicity. GN has become Pigweed's 177primary build system; it is used for all upstream development and strongly 178recommended for Pigweed-based projects where possible. 179 180.. _CMake: https://cmake.org/ 181.. _Bazel: https://bazel.build/ 182.. _GN (Generate Ninja): https://gn.googlesource.com/gn 183.. _Ninja: https://ninja-build.org/ 184 185The GN build 186============ 187This section describes Pigweed's GN build structure, how it is used upstream, 188build conventions, and recommendations for Pigweed-based projects. While 189containing some details about how GN works in general, this section is not 190intended to be a guide on how to use GN. To learn more about the tool itself, 191refer to the official `GN reference`_. 192 193.. _GN reference: https://gn.googlesource.com/gn/+/master/docs/reference.md 194 195.. note:: 196 A quick note on terminology: the word "target" is overloaded within GN (and 197 Pigweed)---it can refer to either a GN build target, such as a ``source_set`` 198 or ``executable``, or to an output platform (e.g. a specific board, device, or 199 system). 200 201 To avoid confusing the two, we refer to the former as "GN targets" and the 202 latter as "Pigweed targets". 203 204Entrypoint: .gn 205--------------- 206The entrypoint to a GN build is the ``.gn`` file, which defines a project's root 207directory (henceforth ``//``). 208 209``.gn`` must point to the location of a ``BUILDCONFIG.gn`` file for the project. 210In Pigweed upstream, this is its only purpose. 211 212Downstream projects may additionally use ``.gn`` to set global overrides for 213Pigweed's build arguments, which apply across all Pigweed targets. For example, 214a project could configure the protobuf libraries that it uses. This is done by 215defining a ``default_args`` scope containing the overrides. 216 217.. code:: 218 219 # The location of the BUILDCONFIG file. 220 buildconfig = "//BUILDCONFIG.gn" 221 222 # Build arguments set across all Pigweed targets. 223 default_args = { 224 dir_pw_third_party_nanopb = "//third_party/nanopb-0.4.2" 225 } 226 227Configuration: BUILDCONFIG.gn 228----------------------------- 229The file ``BUILDCONFIG.gn`` configures the GN build by defining any desired 230global variables/options. It can be located anywhere in the build tree, but is 231conventionally placed at the root. ``.gn`` points GN to this file. 232 233``BUILDCONFIG.gn`` is evaluated before any other GN files, and variables defined 234within it are placed into GN's global scope, becoming available in every file 235without requiring imports. 236 237The options configured in this file differ from those in ``.gn`` in two ways: 238 2391. ``BUILDCONFIG.gn`` is evaluated for every GN toolchain (and Pigweed target), 240 whereas ``.gn`` is only evaluated once. This allows ``BUILDCONFIG.gn`` to set 241 different options for each Pigweed target. 2422. In ``.gn``, only GN build arguments can be overridden. ``BUILDCONFIG.gn`` 243 allows defining arbitrary variables. 244 245Generally, it is preferable to expose configuration options through build args 246instead of globals in ``BUILDCONFIG.gn`` (something Pigweed's build previously 247did), as they are more flexible, greppable, and easier to manage. However, it 248may make sense to define project-specific constants in ``BUILDCONFIG.gn``. 249 250Pigweed's upstream ``BUILDCONFIG.gn`` does not define any variables; it just 251sets Pigweed's default toolchain, which GN requires. 252 253.. _top-level-build: 254 255Top-level GN targets: //BUILD.gn 256-------------------------------- 257The root ``BUILD.gn`` file defines all of the libraries, images, tests, and 258binaries built by a Pigweed project. This file is evaluated immediately after 259``BUILDCONFIG.gn``, with the active toolchain (which is the default toolchain 260at the start of a build). 261 262``//BUILD.gn`` is responsible for enumerating each of the Pigweed targets built 263by a project. This is done by instantiating a version of each of the project's 264GN target groups with each Pigweed target's toolchain. For example, in upstream, 265all of Pigweed's GN targets are contained within the ``pigweed_default`` group. 266This group is instantiated multiple times, with different Pigweed target 267toolchains. 268 269These groups include the following: 270 271* ``host`` -- builds ``pigweed_default`` with Clang or GCC, depending on the 272 platform 273* ``host_clang`` -- builds ``pigweed_default`` for the host with Clang 274* ``host_gcc`` -- builds ``pigweed_default`` for the host with GCC 275* ``stm32f429i`` -- builds ``pigweed_default`` for STM32F429i Discovery board 276* ``docs`` -- builds the Pigweed documentation and size reports 277 278Pigweed projects are recommended to follow this pattern, creating a top-level 279group for each of their Pigweed targets that builds a common GN target with the 280appropriate toolchain. 281 282It is important that no dependencies are listed under the default toolchain 283within ``//BUILD.gn``, as it does not configure any build parameters, and 284therefore should not evaluate any other GN files. The pattern that Pigweed uses 285to achieve this is to wrap all dependencies within a condition checking the 286toolchain. 287 288.. code:: 289 290 group("my_application_images") { 291 deps = [] # Empty in the default toolchain. 292 293 if (current_toolchain != default_toolchain) { 294 # This is only evaluated by Pigweed target toolchains, which configure 295 # all of the required options to build Pigweed code. 296 deps += [ "//images:evt" ] 297 } 298 } 299 300 # The images group is instantiated for each of the project's Pigweed targets. 301 group("my_pigweed_target") { 302 deps = [ ":my_application_images(//toolchains:my_pigweed_target)" ] 303 } 304 305.. warning:: 306 Pigweed's default toolchain is never used, so it is set to a dummy toolchain 307 which doesn't define any tools. ``//BUILD.gn`` contains conditions which check 308 that the current toolchain is not the default before declaring any GN target 309 dependencies to prevent the default toolchain from evaluating any other BUILD 310 files. All GN targets added to the build must be placed under one of these 311 conditional scopes. 312 313"default" group 314^^^^^^^^^^^^^^^ 315The root ``BUILD.gn`` file can define a special group named ``default``. If 316present, Ninja will build this group when invoked without arguments. 317 318.. tip:: 319 Defining a ``default`` group makes using ``pw watch`` simple! 320 321Optimization levels 322^^^^^^^^^^^^^^^^^^^ 323Pigweed's ``//BUILD.gn`` defines the ``pw_default_optimization_level`` build 324arg, which specifies the optimization level to use for the default groups 325(``host``, ``stm32f429i``, etc.). The supported values for 326``pw_default_optimization_level`` are: 327 328* ``debug`` -- create debugging-friendly binaries (``-Og``) 329* ``size_optimized`` -- optimize for size (``-Os``) 330* ``speed_optimized`` -- optimized for speed, without increasing code size 331 (``-O2``) 332 333Pigweed defines versions of its groups in ``//BUILD.gn`` for each optimization 334level. Rather than relying on ``pw_default_optimization_level``, you may 335directly build a group at the desired optimization level: 336``<group>_<optimization>``. Examples include ``host_clang_debug``, 337``host_gcc_size_optimized``, and ``stm32f429i_speed_optimized``. 338 339Upstream GN target groups 340^^^^^^^^^^^^^^^^^^^^^^^^^ 341In upstream, Pigweed splits its top-level GN targets into a few logical groups, 342which are described below. In order to build a GN target, it *must* be listed in 343one of the groups in this file. 344 345apps 346~~~~ 347This group defines the application images built in Pigweed. It lists all of the 348common images built across all Pigweed targets, such as modules' example 349executables. Each Pigweed target can additionally provide its own specific 350images through the ``pw_TARGET_APPLICATIONS`` build arg, which is included by 351this group. 352 353host_tools 354~~~~~~~~~~ 355This group defines host-side tooling binaries built for Pigweed. 356 357pw_modules 358~~~~~~~~~~ 359This group lists the main libraries for all of Pigweed's modules. 360 361pw_module_tests 362~~~~~~~~~~~~~~~ 363All modules' unit tests are collected here, so that they can all be run at once. 364 365pigweed_default 366~~~~~~~~~~~~~~~ 367This group defines everything built in a Pigweed build invocation by collecting 368the above groups and conditionally depending on them based on the active Pigweed 369target's configuration. Generally, new dependencies should not be added here; 370instead, use one of the groups listed above. 371 372The ``pigweed_default`` group is instantiated for each upstream Pigweed target's 373toolchain. 374 375Pigweed target instantiations 376~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 377These groups wrap ``pigweed_default`` with a specific target toolchain. They are 378named after the Pigweed target, e.g. ``host_clang``, ``stm32f429i``, etc. 379 380Other BUILD files: //\*\*/BUILD.gn 381---------------------------------- 382The rest of the ``BUILD.gn`` files in the tree define libraries, configs, and 383build args for each of the modules in a Pigweed project. 384 385Project configuration: //build_overrides/pigweed.gni 386---------------------------------------------------- 387Each Pigweed project must contain a Pigweed configuration file at a known 388location in the GN build tree. Currently, this file only contains a single build 389argument, which must be set to the GN build path to the root of the Pigweed 390repository within the project. 391 392Module variables 393---------------- 394As Pigweed is intended to be a subcomponent of a larger project, it cannot assume 395where it or its modules is located. Therefore, Pigweed's upstream BUILD.gn files 396do not use absolute paths; instead, variables are defined pointing to each of 397Pigweed's modules, set relative to a project-specific ``dir_pigweed``. 398 399To depend on Pigweed modules from GN code, import Pigweed's overrides file and 400reference these module variables. 401 402.. code:: 403 404 # This must be imported before .gni files from any other Pigweed modules. To 405 # prevent gn format from reordering this import, it must be separated by a 406 # blank line from other imports. 407 408 import("//build_overrides/pigweed.gni") 409 410GN target type wrappers 411----------------------- 412To facilitate injecting global configuration options, Pigweed defines wrappers 413around builtin GN target types such as ``source_set`` and ``executable``. These 414are defined within ``$dir_pw_build/target_types.gni``. 415 416.. note:: 417 To take advantage of Pigweed's flexible target configuration system, use 418 ``pw_*`` target types (e.g. ``pw_source_set``) in your BUILD.gn files instead 419 of GN builtins. 420 421Pigweed targets 422--------------- 423To build for a specific hardware platform, builds define Pigweed targets. These 424are essentially GN toolchains which set special arguments telling Pigweed how to 425build. For information on Pigweed's target system, refer to 426:ref:`docs-targets`. 427 428The dummy toolchain 429------------------- 430Pigweed's ``BUILDCONFIG.gn`` sets the project's default toolchain to a "dummy" 431toolchain which does not specify any compilers or override any build arguments. 432Downstream projects are recommended to do the same, following the steps 433described in :ref:`top-level-build` to configure builds for each of their 434Pigweed targets. 435 436.. admonition:: Why use a dummy? 437 438 To support some of its advanced (and useful!) build features, Pigweed requires 439 the ability to generate new toolchains on the fly. This requires having 440 knowledge of the full configuration of the current toolchain (which is easy if 441 it's all defined within a scope), something which is impractical to achieve 442 using the default toolchain. 443 444 Additionally, there are some cases where GN treats default and non-default 445 toolchains differently. By not using the default toolchain, we avoid having 446 to deal with these inconsistencies. 447 448 It is possible to build Pigweed using only the default toolchain, but it 449 requires a more complicated setup to get everything working and should be 450 avoided unless necessary (for example, when integrating with a large existing 451 GN-based project). 452 453Upstream development examples 454----------------------------- 455If developing for upstream Pigweed, some common build use cases are described 456below. 457 458Building a custom executable/app image 459^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 460 4611. Define your executable GN target using the ``pw_executable`` template. 462 463 .. code:: 464 465 # //foo/BUILD.gn 466 pw_executable("foo") { 467 sources = [ "main.cc" ] 468 deps = [ ":libfoo" ] 469 } 470 4712. In the root ``BUILD.gn`` file, add the executable's GN target to the ``apps`` 472 group. 473 474 .. code:: 475 476 # //BUILD.gn 477 group("apps") { 478 deps = [ 479 # ... 480 "//foo", # Shorthand for //foo:foo 481 ] 482 } 483 4843. Run the ninja build to compile your executable. The apps group is built by 485 default, so there's no need to provide a target. The executable will be 486 compiled for every supported Pigweed target. 487 488 .. code:: 489 490 ninja -C out 491 492 Alternatively, build your executable by itself by specifying its path to 493 Ninja. When building a GN target manually, the Pigweed target for which it 494 is built must be specified on the Ninja command line. 495 496 For example, to build for the Pigweed target ``host_gcc_debug``: 497 498 .. code:: 499 500 ninja -C out host_gcc_debug/obj/foo/bin/foo 501 502 .. note:: 503 504 The path passed to Ninja is a filesystem path within the ``out`` directory, 505 rather than a GN path. This path can be found by running ``gn outputs``. 506 5074. Retrieve your compiled binary from the out directory. It is located at the 508 path 509 510 .. code:: 511 512 out/<pw_target>/obj/<gn_path>/{bin,test}/<executable> 513 514 where ``pw_target`` is the Pigweed target for which the binary was built, 515 ``gn_path`` is the GN path to the BUILD.gn file defining the executable, 516 and ``executable`` is the executable's GN target name (potentially with an 517 extension). Note that the executable is located within a ``bin`` subdirectory 518 in the module (or ``test`` for unit tests defined with ``pw_test``). 519 520 For example, the ``foo`` executable defined above and compiled for the 521 Pigweed target stm32f429i_disc1_debug is found at: 522 523 .. code:: 524 525 out/stm32f429i_disc1_debug/obj/foo/bin/foo 526