This entry provides instructions for working with Embedded Artistry’s standardized Meson build system. This build system is used on the majority of our projects and provides a common interface and framework for building software and enforcing code quality. For a simplified example, see our project skeleton. The reusable build modules are found in the meson-buildsystem repository.
Instructions on installing dependencies used by all of our projects is included at the end of these instructions.
If you are interested in learning the Meson build system, we recommend our course Creating a Cross-Platform Build System for Embedded Projects with Meson, which teaches you Meson by buildling a complex build system from scratch. The modules used in our build system are explained in detail in the follow-on course, Building a Reusable Project Skeleton with Meson.
Table of Contents:
- Building the Project
- Cross-Compilation
- Using Alternate Build Machine Toolchains
- Configuring a Build
- Supporting Tooling
- Dependencies
Building the Project
There are two ways to build our projects:
Makefile Interface
Our preferred way to interact with the build system is through the standardized Makefile interface, which provides convenient one-touch commands for interacting with the build system.
Project targets can be built by issuing the following command:
make
You can clean build targets using:
make clean
You can eliminate the generated buildresults folder using:
make distclean
Full information on support commands and configuration options can be found by issuing:
make help
Additional commands and options for this interface are discussed in:
Meson Interface
You can also work natively with the Meson build system.
Configure a build output folder with meson:
meson buildresults
Use ninja to build targets within the build output folder:
ninja -C buildresults
# OR
cd buildresults
ninja
The Make command shim still works with configured build output folders.
Cross-Compilation
Cross-compilation in Meson is handled through cross files. These files must be specified when configuring a build output folder. You can find the files that we use in build/cross. You will likely need to supply your own cross file for your particular processor and floating-point configuration.
meson buildresults --cross-file=path/to/your/cross-file.txt
You can also pass this option using the Make interface with the OPTIONS variable:
make OPTIONS=--cross-file=path/to/your/cross-file.txt
If you’re using a cross file that we provide in the build system, you can also use the CROSS argument. This should be the filename without the path or .txt extension.
make CROSS=nrf52840.txt
Cross files can be “layered”, where multiple cross files are specified. Later cross-files override the values of earlier files. We take advantage of this by defining a common arm.txt file which specifies the toolchain. Processor-specific flags are contained in a secondary file.
meson buildresults --cross-file=build/cross/arm.txt --cross-file=build/cross/cortex-m3.txt
You can also layer cross files with the CROSS option. Separate cross files with a colon (:):
make CROSS=arm:cortex-m3
Using Alternate Build Machine Toolchains
You can specify alternate build machine build environments using native files. You can use native files to customize the build settings used for non-cross-compiled targets. You can find files for toolchains that we use in build/native.
Native file can also layered similarly to cross files. When using the NATIVE variable, use a colon (:) to indicate layered files.
meson buildresults --native-file=build/native/gcc-9.txt --native-file=build/native/gcc-gold.txt
make NATIVE=gcc-9:gcc-gold
Configuring a Build
Meson provides a number of built-in options, and many of our projects provide custom options (specified in meson_options.txt).
These options are typically set when configuring a build output folder. Options can be specified using -Dwith the option name and a value to set:
meson buildresults -Ddisable-builtins=true
If you want to change an options for an already-configured build output directory, you must use the meson configure command inside of that directory:
cd buildresults
meson configure -Ddisable-builtins=true
You can run meson configure without any options to see how the current build output folder is configured.
Options can be specified with the Makefile interface in a number of ways.
You can pass raw options to Meson with the OPTIONS variable:
make OPTIONS=-Ddisable-builtins=true
Some options are binary (0/1), such as LTO=1 for enabling link-time optimization and DEBUG=1 for enabling debug build configurations.
You can also configure a build using Clang/GCC sanitizers with the SANITIZER variable. Options are none (default), address, thread, undefined, memory, and address,undefined.
make SANITIZER=address
Common Meson Options
Position Independent Code (PIC) is enabled by default, but can be disabled by setting the built-in option b_staticpic to false:
meson buildresults -Db_staticpic=false
Supporting Tooling
Our build system makes use of a variety of supporting tools:
Static Analysis
Our projects support a variety of static analysis tools. These tools are integrated into the build system and used to enforce project quality.
- Cppcheck has two targets, which differ in the output:
cppcheck– terminal outputcppcheck-xml– XML output- These commands are the same for the Make and Ninja interfaces
- Clang scan-build has a single target that is the same in Make and Ninja:
scan-build - We use Lizard for complexity analysis, which measures argument counts, function lengths, and cyclomatic complexity. Three targets are provided, which differ in the output:
complexity– only prints violationscomplexity-full– prints the full complexity reportcomplexity-xml– generates a full complexity report in XML format
- Clang-tidy has a single target.
tidyis the Makefile targetclang-tidyis the Ninja target
Code Formatting
Code formatting is handled in our projects using clang-format. The style guidelines are documented in a .clang-format file at the root of the project.
You can auto-format your code to match the style guidelines by issuing the following command:
make format
The format-patch command is used by the CI system. If formatting changes are needed, they will be uploaded to the build server in a .patch file that can be manually applied to your PR in the event that you do not have clang-format installed.
Code Coverage
Our build system leverages Meson’s built-in support for coverage generation using gcovr and lcov.
The best way to do this is to use the Makefile shim, which runs all necessary commands:
make coverage
If you wish to manually configure coverage analysis with Meson, you will need to perform the following steps:
# Configure the build
meson buildresults -Db_coverage=true
# Run tests to generate coverage information
ninja -C buildresults test
# Prepare reports
ninja -C buildresults coverage
Documentation
Documentation on our projects is generated using Doxygen. You can generate HTML project documentation for our projects using:
make docs # top-level
ninja docs # in build output folder
The documentation will be placed in a docs/ subdirectory of the configured build output folder.
We use two additional tools to document our programs:
- adr-tools, which is used to document the rationale behind major design decisions
- Pottery, which is used to record major events in our projects
The output of these tools feeds into the overall project documentation.
adr-tools
We use Architecture Decision Records in our projects to document major changes or design decisions for a project.
To document a new architectural or design decision, use:
adr new Title of the Decision Entry
An editor will open, and you must fill out the skeleton with relevant information. Please be as detailed as possible, since these are records of why we made a design decision.
You can see a list of all registered decisions:
adr list
docs/architecture/decisions/0001-record-architecture-decisions.md
docs/architecture/decisions/0002-expect-external-libc-to-be-supplied-through-subproject-or-cross-file.md
docs/architecture/decisions/0003-locking-approach-for-malloc-freelist.md
Individual records can be viewed by opening the Markdown file. They are also included in project documentation output.
ADRs can be linked together with the adr link command. Linking ADRs should be done when one ADR supersedes, complements, or references another. This information is used to generate the ADR relational graph during the documentation generation process.
adr help link
usage: adr link SOURCE LINK TARGET REVERSE-LINK
Creates a link between two ADRs, from SOURCE to TARGET new.
SOURCE and TARGET are both a reference (number or partial filename) to an ADR
LINK is the description of the link created in the SOURCE.
REVERSE-LINK is the description of the link created in the TARGET
E.g. to create link ADR 12 to ADR 10
adr link 12 Amends 10 "Amended by"
Pottery
We use pottery to keep a log of major events that occur in the development of a project. This is a form of history tracking, allowing us to note down external factors that may impact development but not be captured elsewhere in the documentation.
To note a new event in the project log, use:
pottery note # opens an editor
pottery note "Post this message"
To see all of the notes in a project, use:
pottery show
Dependencies
There are three major categories of dependencies for working with our build system:
- Build system dependencies, which are used to configure the project and manage the build process
- Compiler dependencies, which are the toolchains supported by our projects
- Tooling dependencies, which are the tools used in supporting targets (e.g. static analysis, auto-formatting, documentation generation)
Build System Dependencies
- Meson
- Make is required to use Makefile shims
- pkg-config
Meson
Meson is the primary build system configuration tool. It is implemented in Python. You will need to install python3 and pip3 if they are not already installed on your system. You will also need the Ninja build system, which is the default target for Meson.
To install these programs on MacOS, use the following commands with Homebrew:
brew install python3 ninja
To install these programs on Linux/WSL:
sudo apt install python3 python3-pip ninja-build
You can then install Meson with pip3:
pip3 install meson
If you want to install Meson globally on Linux/WSL, use:
sudo -H pip3 install meson
Make
We use Make to provide a standardized command interface to build all of our projects, as well as to provide “one touch” commands.
If you are a Linux or WSL user and installed the build-essential package, Make should already be installed. If not, you can install it manually with:
sudo apt install make
If you’re a MacOS user and you’ve installed XCode command line tools, you will already have Make and the necessary binutils programs.
pkg-config
We occasionally leverage pkg-config to find some dependencies within our build system.
To install it on Linux/WSL:
sudo apt install pkg-config
To install on MacOS:
brew install pkg-config
Compiler Dependencies
Our projects are typically tested against the following compilers:
- Apple Clang
- Mainline Clang
- Latest GCC
- GCC-11, -10, -9
- gnu-arm-none-eabi latest
- gnu-arm-none-eabi-10, -9
Tooling Dependencies
We leverage a variety of tools in our build system modules. The following dependencies are optional on your system. If a tool is not installed, its build targets will be unavailable on your machine. If you are contributing to Embedded Artistry projects, we recommend installing all of the dependencies.
- Doxygen
- CppCheck
- clang-format
- clang-tidy
- gcovr
- lcov
- genhtml
- scan-build (Clang)
- lizard
- adr-tools
- pottery
Lizard can be installed with pip:
pip3 install lizard
To install all tooling dependencies on Linux/WSL, use:
sudo apt install doxygen cppcheck gcovr lcov clang-format clang-tidy clang-tools
genhtml is part of lcov. scan-build is part of clang-tools, which you may have already installed when setting up the toolchain.
To install all tooling dependencies on MacOS, use:
brew install doxygen cppcheck clang-format gcovr lcov
genhtml is part of lcov.
clang-tidy and scan-build must be installed using the llvm toolchain package:
brew install llvm
If you are using MacOS, you can install adr-tools through Homebrew:
brew install adr-tools
If you are using Windows or Linux, please install adr-tools via GitHub.
To install pottery, please follow the instructions on GitHub.

How can I download this build system? I don’t see a link anywhere.
I’ll get it added to the article.
https://github.com/embeddedartistry/meson-buildsystem – the reusable components
https://github.com/embeddedartistry/project-skeleton – basic skeleton using the system
and most of our individual projects on GitHub use it