Skip to content

How to add a new software component

Overview

The Open IoT SDK provides a collection of software components. They are specified by CMake scripts in the components directory and downloaded as part of the build process.

This guide explains how to contribute a new component to the Open IoT SDK.

Tip

This guide only applies to the process of adding components to the Open IoT SDK. If a user intends to add a component to their own project only, they can do so in any way that is valid in CMake without having to modify the Open IoT SDK.

Fetching

Components that offer major features (e.g. cloud service provider clients, storage APIs, etc.) are located in their respective repositories and fetched by the Open IoT SDK. The Open IoT SDK itself only provides integration with those repositories.

Example

Take Mbed TLS for example:

Note

The order of the add_subdirectory() calls matters in components/CMakeLists.txt. For example, aws-iot-device-sdk-embedded-c depends on mbedtls, so mbedtls must be made available first.

CMake support

Repositories with built-in CMake support

During the build process, if a fetched repository contains a CMakeLists.txt in its root directory, it will be automatically processed by the FetchContent_MakeAvailable() call.

The repository's CMake support defines necessary CMake targets such as libraries.

Example

The Mbed TLS repository comes with CMake support and defines the libraries mbedtls, mbedx509 and mbedcrypto. Applications and other libraries can link against any of those through target_link_libraries() in CMake.

Repositories without CMake support

If the fetched repository has no CMake support, we need to create CMake targets by ourselves in the Open IoT SDK.

Example

The CMSIS Version 5 repository does not support CMake, but we need the CMSIS-RTOS v2 code from it.

To add CMake support, the Open IoT SDK's components/cmsis_5/CMakeLists.txt defines a library cmsis-rtos-api that points to the source files and include directory inside the fetched repository:

add_library(cmsis-rtos-api
    ${cmsis-5_SOURCE_DIR}/CMSIS/RTOS2/Source/os_systick.c
    ${cmsis-5_SOURCE_DIR}/CMSIS/RTOS2/Source/os_tick_ptim.c
)
target_include_directories(cmsis-rtos-api
    PUBLIC
        ${cmsis-5_SOURCE_DIR}/CMSIS/RTOS2/Include
)

The variable cmsis-5_SOURCE_DIR used above contains the absolute path to the fetched repository. It is set by FetchContent_MakeAvailable(cmsis-5) earlier in the CMakeLists.txt.

Configuration

Most components can be configured to cater for different use cases.

Some components come with CMake options for configuration. For example, Azure RTOS ThreadX provides a TX_USER_FILE option, which a user can set to their own tx_user.h header's path that overrides RTOS parameters. In this case, we simply let user projects use the component's built-in CMake options.

For a component that does not come with CMake configuration options, we should

  1. declare a configuration target, add_library(<component-name>-config INTERFACE), where <component-name> is the component's name
  2. link each library of the component against <component-name>-config, so that the library picks up the configuration
  3. mention in the documentation that a user application can add compile-time macros or include paths to this target.

Example

The Open IoT SDK's support for CMSIS Version 5 in components/cmsis_5/:

  • declares the configuration target cmsis-config in CMakeLists.txt
  • links the existing library iotsdk-cmsis-core-device against cmsis-config in that CMakeLists.txt
  • documents the usage of cmsis-config in keil_rtx_v5.md

Integration layers (glue) between components

Some components have been pre-integrated with one another via integration layers, also informally known as glue. An integration layer may have been provided by a fetched repository already, but if this is not the case, we can implement our own integration layer and ship the code in the Open IoT SDK.

Example

An implementation of Mbed TLS multithreading support based on CMSIS-RTOS v2 is

The code is entirely in the Open IoT SDK repository.

Custom builds

Certain components, such as TrustedFirmware-M and ML Embedded Evaluation Kit, must be built as standalone projects, separately from the Open IoT SDK itself.

Info

Those components allow only standalone builds, because their build options must not be intermingled with those from other projects (such as the Open IoT SDK). This ensures architectural features are enabled correctly for optimal security (in the case of TrustedFirmware-M) or performance (in the case of ML Embedded Evaluation Kit), exactly as they have been tested.

The Open IoT SDK sets the custom build command, triggers the custom build and makes use of the build outputs once it completes. This is done using CMake's ExternalProject modules.

Example

TrustedFirmware-M is built by the Open IoT SDK as follows:

The script components/trusted-firmware-m/CMakeLists.txt includes BuildTfm.cmake which calls ExternalProject_Add() to specify how to build TrustedFirmware-M.

To understand what each parameter does, see the documentation for ExternalProject. Those detailed in the following paragraphs are particularly important.

Because the repository is fetched using the FetchContent module as described above, the SOURCE_DIR and BINARY_DIR parameters for the build are set to ${trusted-firmware-m_SOURCE_DIR} and ${trusted-firmware-m_BINARY_DIR} which are the directories created by the FetchContent module.

Note

This should be done for all ExternalProject builds with the SDK. The reason is that we prefer FetchContent over ExternalProject to handle fetching, due to all the benefits described in Build Concepts.

By default, ExternalProject silently performs the custom build in the background and suppresses the build's terminal output until done. This may give the user a false impression that the build is stuck. To print the terminal output in real time, allowing the user to see the progress, we set the following parameters in the body of the ExternalProject_Add() call:

USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD     ON

Tip

Some projects require additional steps added by ExternalProject_Add_Step(), and each additional step's terminal output can be enabled in the body of the ExternalProject_Add_Step() call:

USES_TERMINAL ON

An example is BuildMlEmbeddedEvaluationKit.cmake.

By default, ExternalProject performs the custom build only once, and it does not do any rebuild even after the user has modified the code. To ensure the build is performed every time, allowing any code changes to be picked up, we set the following parameter:

BUILD_ALWAYS ON

Note

This enables an incremental rebuild rather than a clean build, so its impact on build time is minimal.

TrustedFirmware-M is a CMake projects, so we use the CMAKE_ARGS parameter to specify the CMake command line arguments for the build.

Tip

You can also build a non-CMake project by specifying the BUILD_COMMAND parameter instead of CMAKE_ARGS.

BUILD_BYPRODUCTS lists build outputs that we will make use of, i.e. files that are referenced in the Open IoT SDK's CMake scripts. By default, CMake checks that any referenced files exist before build starts, and BUILD_BYPRODUCTS disables upfront checks on files that will be generated later.

Warning

If a file is missing from BUILD_BYPRODUCTS, you will get a CMake error saying the file cannot be found.