Zephyr CMake Package

The Zephyr CMake package is a convenient way to create a Zephyr-based application.

The Zephyr CMake package ensures that CMake can automatically select a Zephyr to use for building the application, whether it is a Zephyr repository application, Zephyr workspace application, or a Zephyr freestanding application.

When developing a Zephyr-based application, then a developer simply needs to write find_package(Zephyr) in the beginning of the application CMakeLists.txt file.

To use the Zephyr CMake package it must first be exported to the CMake user package registry. This is means creating a reference to the current Zephyr installation inside the CMake user package registry.

In Linux, the CMake user package registry is found in:

~/.cmake/package/Zephyr

The Zephyr CMake package allows CMake to automatically find a Zephyr base. One or more Zephyr installations must be exported. Exporting multiple Zephyr installations may be useful when developing or testing Zephyr freestanding applications, Zephyr workspace application with vendor forks, etc..

Zephyr CMake package export (west)

When installing Zephyr using west then it is recommended to export Zephyr using west zephyr-export.

Zephyr CMake package export (without west)

Zephyr CMake package is exported to the CMake user package registry using the following commands:

cmake -P <PATH-TO-ZEPHYR>/share/zephyr-package/cmake/zephyr_export.cmake

This will export the current Zephyr to the CMake user package registry.

To also export the Zephyr Unittest CMake package, run the following command in addition:

cmake -P <PATH-TO-ZEPHYR>/share/zephyrunittest-package/cmake/zephyr_export.cmake

Zephyr application structure

An application can be placed anywhere on your disk, but to better understand how the Zephyr package is used, we will name three specific layouts.

Zephyr repository application

A Zephyr repository has the following structure:

<projects>/zephyr-workspace
└── zephyr
     ├── arch
     ├── boards
     ├── cmake
     ├── samples
     │    ├── hello_world
     │    └── ...
     ├── tests
     └── ...

Any application located inside this tree, is simply referred to as a Zephyr repository application. In this example hello_world is a Zephyr repository application.

Zephyr workspace application

A Zephyr workspace has the following structure:

<projects>/zephyr-workspace
├── zephyr
├── bootloader
├── modules
├── tools
├── <vendor/private-repositories>
└── my_applications
     └── my_first_app

Any application located in such workspace, but outside the Zephyr repository itself, is referred to as a Zephyr workspace application. In this example my_first_app is a Zephyr workspace application.

Note

The root of a Zephyr workspace is identical to west topdir if the workspace was installed using west

Zephyr freestanding application

A Zephyr freestanding application is a Zephyr application located outside of a Zephyr workspace.

<projects>/zephyr-workspace
├── zephyr
├── bootloader
└── ...

<home>/app
├── CMakeLists.txt
├── prj.conf
└── src
    └── main.c

In this example app is a Zephyr freestanding application.

Zephyr Base Environment Setting

The Zephyr CMake package search functionality allows for explicitly specifying a Zephyr base using an environment variable.

To do this, use the following find_package() syntax:

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

This syntax instructs CMake to first search for Zephyr using the Zephyr base environment setting ZEPHYR_BASE and then use the normal search paths.

Zephyr CMake Package Search Order

When Zephyr base environment setting is not used for searching, the Zephyr installation matching the following criteria will be used:

  • A Zephyr repository application will use the Zephyr in which it is located. For example:

    <projects>/zephyr-workspace/zephyr
    └── samples
        └── hello_world
    

    in this example, hello_world will use <projects>/zephyr-workspace/zephyr.

  • Zephyr workspace application will use the Zephyr that share the same workspace. For example:

    <projects>/zephyr-workspace
    ├── zephyr
    ├── ...
    └── my_applications
         └── my_first_app
    

    in this example, my_first_app will use <projects>/zephyr-workspace/zephyr as this Zephyr is located in the same workspace as the Zephyr workspace application.

  • Zephyr freestanding application will use the Zephyr registered in the CMake user package registry. For example:

    <projects>/zephyr-workspace-1
    └── zephyr                       (Not exported to CMake)
    
    <projects>/zephyr-workspace-2
    └── zephyr                       (Exported to CMake)
    
    <home>/app
    ├── CMakeLists.txt
    ├── prj.conf
    └── src
        └── main.c
    

    in this example, only <projects>/zephyr-workspace-2/zephyr is exported to the CMake package registry and therefore this Zephyr will be used by the Zephyr freestanding application <home>/app.

    If user wants to test the application with <projects>/zephyr-workspace-1/zephyr, this can be done by using the Zephyr Base environment setting, meaning set ZEPHYR_BASE=<projects>/zephyr-workspace-1/zephyr, before running CMake.

    Note

    The Zephyr package selected on the first CMake invocation will be used for all subsequent builds. To change the Zephyr package, for example to test the application using Zephyr base environment setting, then it is necessary to do a pristine build first (See Rebuilding an Application).

Zephyr CMake Package Version

When writing an application then it is possible to specify a Zephyr version number x.y.z that must be used in order to build the application.

Specifying a version is especially useful for a Zephyr freestanding application as it ensures the application is built with a minimal Zephyr version.

It also helps CMake to select the correct Zephyr to use for building, when there are multiple Zephyr installations in the system.

For example:

find_package(Zephyr 2.2.0)
project(app)

will require app to be built with Zephyr 2.2.0 as minimum. CMake will search all exported candidates to find a Zephyr installation which matches this version criteria.

Thus it is possible to have multiple Zephyr installations and have CMake automatically select between them based on the version number provided, see CMake package version for details.

For example:

<projects>/zephyr-workspace-2.a
└── zephyr                       (Exported to CMake)

<projects>/zephyr-workspace-2.b
└── zephyr                       (Exported to CMake)

<home>/app
├── CMakeLists.txt
├── prj.conf
└── src
    └── main.c

in this case, there are two released versions of Zephyr installed at their own workspaces. Workspace 2.a and 2.b, corresponding to the Zephyr version.

To ensure app is built with minimum version 2.a the following find_package syntax may be used:

find_package(Zephyr 2.a)
project(app)

Note that both 2.a and 2.b fulfill this requirement.

CMake also supports the keyword EXACT, to ensure an exact version is used, if that is required. In this case, the application CMakeLists.txt could be written as:

find_package(Zephyr 2.a EXACT)
project(app)

In case no Zephyr is found which satisfies the version required, as example, the application specifies

find_package(Zephyr 2.z)
project(app)

then an error similar to below will be printed:

Could not find a configuration file for package "Zephyr" that is compatible
with requested version "2.z".

The following configuration files were considered but not accepted:

  <projects>/zephyr-workspace-2.a/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake, version: 2.a.0
  <projects>/zephyr-workspace-2.b/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake, version: 2.b.0

Note

It can also be beneficial to specify a version number for Zephyr repository applications and Zephyr workspace applications. Specifying a version in those cases ensures the application will only build if the Zephyr repository or workspace is matching. This can be useful to avoid accidental builds when only part of a workspace has been updated.

Multiple Zephyr Installations (Zephyr workspace)

Testing out a new Zephyr version, while at the same time keeping the existing Zephyr in the workspace untouched is sometimes beneficial.

Or having both an upstream Zephyr, Vendor specific, and a custom Zephyr in same workspace.

For example:

<projects>/zephyr-workspace
├── zephyr
├── zephyr-vendor
├── zephyr-custom
├── ...
└── my_applications
     └── my_first_app

in this setup, find_package(Zephyr) has the following order of precedence for selecting which Zephyr to use:

  • Project name: zephyr

  • First project, when Zephyr projects are ordered lexicographical, in this case.

    • zephyr-custom

    • zephyr-vendor

This means that my_first_app will use <projects>/zephyr-workspace/zephyr.

It is possible to specify a Zephyr preference list in the application.

A Zephyr preference list can be specified as:

set(ZEPHYR_PREFER "zephyr-custom" "zephyr-vendor")
find_package(Zephyr)

project(my_first_app)

the ZEPHYR_PREFER is a list, allowing for multiple Zephyrs. If a Zephyr is specified in the list, but not found in the system, it is simply ignored and find_package(Zephyr) will continue to the next candidate.

This allows for temporary creation of a new Zephyr release to be tested, without touching current Zephyr. When testing is done, the zephyr-test folder can simply be removed. Such a CMakeLists.txt could look as:

set(ZEPHYR_PREFER "zephyr-test")
find_package(Zephyr)

project(my_first_app)

Zephyr Build Configuration CMake package

The Zephyr Build Configuration CMake package provides a possibility for a Zephyr based project to control Zephyr build settings in a generic way.

It is similar to the per-user .zephyrrc file that can be used to set Environment Variables, but it sets CMake variables instead. It also allows you to automatically share the build configuration among all users through the project repository. It also allows more advanced use cases, such as loading of additional CMake boilerplate code.

The Zephyr Build Configuration CMake package will be loaded in the Zephyr boilerplate code after initial properties and ZEPHYR_BASE has been defined, but before CMake code execution. This allows the Zephyr Build Configuration CMake package to setup or extend properties such as: DTS_ROOT, BOARD_ROOT, TOOLCHAIN_ROOT / other toolchain setup, fixed overlays, and any other property that can be controlled. It also allows inclusion of additional boilerplate code.

To provide a Zephyr Build Configuration CMake package, create ZephyrBuildConfig.cmake and place it in a Zephyr workspace top-level folder as:

<projects>/zephyr-workspace
├── zephyr
├── ...
└── zephyr application (can be named anything)
     └── share/zephyrbuild-package/cmake/ZephyrBuildConfig.cmake

The Zephyr Build Configuration CMake package will not search in any CMake default search paths, and thus cannot be installed in the CMake package registry. There will be no version checking on the Zephyr Build Configuration package.

Note

share/zephyrbuild-package/cmake/ZephyrBuildConfig.cmake follows the same folder structure as the Zephyr CMake package.

It is possible to place ZephyrBuildConfig.cmake directly in a <zephyr application>/cmake folder or another folder, as long as that folder is honoring the CMake package search algorithm.

A sample ZephyrBuildConfig.cmake can be seen below.

# ZephyrBuildConfig.cmake sample code

# To ensure final path is absolute and does not contain ../.. in variable.
get_filename_component(APPLICATION_PROJECT_DIR
                       ${CMAKE_CURRENT_LIST_DIR}/../../..
                       ABSOLUTE
)

# Add this project to list of board roots
list(APPEND BOARD_ROOT ${APPLICATION_PROJECT_DIR})

# Default to GNU Arm Embedded toolchain if no toolchain is set
if(NOT ENV{ZEPHYR_TOOLCHAIN_VARIANT})
    set(ZEPHYR_TOOLCHAIN_VARIANT gnuarmemb)
    find_program(GNU_ARM_GCC arm-none-eabi-gcc)
    if(NOT ${GNU_ARM_GCC} STREQUAL GNU_ARM_GCC-NOTFOUND)
        # The toolchain root is located above the path to the compiler.
        get_filename_component(GNUARMEMB_TOOLCHAIN_PATH ${GNU_ARM_GCC}/../.. ABSOLUTE)
    endif()
endif()

Zephyr Build Configuration CMake package (Freestanding application)

The Zephyr Build Configuration CMake package can be located outside a Zephyr workspace, for example located with a Zephyr freestanding application.

Create the build configuration as described in the previous section, and then refer to the location of your Zephyr Build Configuration CMake package using the CMake variable ZephyrBuildConfiguration_ROOT.

  1. At the CMake command line, like this:

    cmake -DZephyrBuildConfiguration_ROOT=<path-to-build-config> ...
    
  2. At the top of your application’s top level CMakeLists.txt, like this:

    set(ZephyrBuildConfiguration_ROOT <path-to-build-config>)
    find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
    

    If you choose this option, make sure to set the variable before calling find_package(Zephyr ...), as shown above.

  3. In a separate CMake script which is pre-loaded to populate the CMake cache, like this:

    # Put this in a file with a name like "zephyr-settings.cmake"
    set(ZephyrBuildConfiguration_ROOT <path-to-build-config>
        CACHE STRING "pre-cached build config"
    )
    

    You can tell the build system to use this file by adding -C zephyr-settings.cmake to your CMake command line. This principle is useful when not using west as both this setting and Zephyr modules can be specified using the same file. See Zephyr module Without West.

Zephyr CMake package source code

The Zephyr CMake package source code in <PATH-TO-ZEPHYR>/share/zephyr-package/cmake contains the CMake config package which is used by CMake find_package function.

It also contains code for exporting Zephyr as a CMake config package.

The following is an overview of those files

CMakeLists.txt

The CMakeLists.txt file for the CMake build system which is responsible for exporting Zephyr as a package to the CMake user package registry.

ZephyrConfigVersion.cmake

The Zephyr package version file. This file is called by CMake to determine if this installation fulfils the requirements specified by user when calling find_package(Zephyr ...). It is also responsible for detection of Zephyr repository or workspace only installations.

ZephyrUnittestConfigVersion.cmake

Same responsibility as ZephyrConfigVersion.cmake, but for unit tests. Includes ZephyrConfigVersion.cmake.

ZephyrConfig.cmake

The Zephyr package file. This file is called by CMake to for the package meeting which fulfils the requirements specified by user when calling find_package(Zephyr ...). This file is responsible for sourcing of boilerplate code.

ZephyrUnittestConfig.cmake

Same responsibility as ZephyrConfig.cmake, but for unit tests. Includes ZephyrConfig.cmake.

zephyr_package_search.cmake

Common file used for detection of Zephyr repository and workspace candidates. Used by ZephyrConfigVersion.cmake and ZephyrConfig.cmake for common code.

pristine.cmake

Pristine file for removing all files created by CMake during configure and generator time when exporting Zephyr CMake package. Running pristine keeps all package related files mentioned above.