Embedded Artistry’s Standardized Meson Build System

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.

Note

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:

  1. Building the Project
    1. Makefile Interface
    2. Meson Interface
  2. Cross-Compilation
  3. Using Alternate Build Machine Toolchains
  4. Configuring a Build
    1. Common Meson Options
  5. Supporting Tooling
    1. Static Analysis
    2. Code Formatting
    3. Code Coverage
    4. Documentation
    5. adr-tools
    6. Pottery
  6. Dependencies
    1. Build System Dependencies
      1. Meson
      2. Make
      3. pkg-config
    2. Compiler Dependencies
    3. Tooling Dependencies

Building the Project

There are two ways to build our projects:

  1. A standardized Makefile interface
  2. Working natively with Meson

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:

  1. Cross-Compilation
  2. Using Alternate Build Machine Toolchains
  3. Configuring a Build
  4. Supporting Tooling

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:

  1. Static Analysis
  2. Code Formatting
  3. Code Coverage
  4. Documentation
  5. adr-tools
  6. Pottery

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 output
    • cppcheck-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 violations
    • complexity-full – prints the full complexity report
    • complexity-xml – generates a full complexity report in XML format
  • Clang-tidy has a single target.
    • tidy is the Makefile target
    • clang-tidy is 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:

  1. adr-tools, which is used to document the rationale behind major design decisions
  2. 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

  1. Meson
  2. Make is required to use Makefile shims
  3. 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
Note

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
Note

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.

2 Replies to “Embedded Artistry’s Standardized Meson Build System”

Share Your Thoughts

This site uses Akismet to reduce spam. Learn how your comment data is processed.