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