Building with CMake

CMake is a system that manages the build process. It is cross-platform system but designed to be used in conjunction with the native build environment.

Prerequisites

  1. Download and install CMake. You can find the minimum required CMake version in the first line of ./CMakeLists.txt in the root of this repository. On windows, make sure that the installation wizard adds CMake to the PATH variable.

  2. Some build steps also require python 3. Make sure that some python 3 version is installed (e.g. Python 3.8.1).

  3. (For Simulator) Make sure a C++17 32 bit compatible compiler is installed (e.g. Visual Studio 2017, GCC 8, or Clang 9)

  4. (For Firmware) Make sure the correct Embedded Toolchain is installed, as described in Quick Start.

Simulator

Creating native build files for x86

As is always the case with CMake, an "out-of-source build" should be preferred. This means that you create the project files in a directory that is separate from the rest of the repository. While it is possible to create such an "out-of-source build" in a directory within the repository, a sibling directory is preferred as some build configurations might not work properly with in-repo directories.

  1. Create a sibling directory next to the repository

  2. Open the Command Line / Terminal in this directory

  3. Execute the following: cmake ../fruitymesh -DBUILD_TYPE=SIMULATOR (assuming the repository has the default "fruitymesh" name).

On Windows, we do recommend that you download the free Visual Studio Community Edition. The Windows 10 SDK as well as C++ Build Tools need to be installed. In order to create the build files, you should use cmake ../fruitymesh -A Win32 -DBUILD_TYPE=SIMULATOR which will generate the whole Visual Studio Solution for you that you only need to open and hit Play.

The necessary build files are created inside the sibling directory. To make sure that everything worked as intended try to compile and run the cherrySim_runner and cherrySim_tester projects. In case you are using visual studio, open the Solution file (.sln), right click the projects on the left side and select "start up project".

Firmware

Creating native build files for the MCU firmware

Building the native featuresets for your development board is mostly the same as with the simulator. The only difference is that some extra parameters need to be passed to cmake:

cmake ../fruitymesh -DBUILD_TYPE=FIRMWARE -DGCC_PATH="C:/path/to/gcc-arm-none-eabi-4_9" -G "Unix Makefiles"

The GCC_PATH must be specified using forward slashes ("/"), not backward slashes ("\"). If you accidentally used backward slashes you have to remove all the files that CMake just created in the current working directory and execute the command again with forward slashes.

Executing the compiler

Once the native build files are created, it is possible to create all the executables without interacting with the native compiler directly. To do this, while being in the directory where you created the native build files, execute cmake --build .. If you are using visual studio, the .exe files are then located inside the Debug directory.

Troubleshooting

Simulator must be compiled as a 32 bit binary

32 bit compilation must be available. On some systems this is not the case by default. If you are using Visual Studio, the flag -A Win32 forces cmake to generate a 32 bit compatible solution.

Generated Visual Studio project must be started with the correct version

If several visual studio versions are installed (e.g. 2017 + 2019), make sure that you are starting the solution file with the correct visual studio version. The one that is displayed by CMake while it generates the project.

Invalid character escape can be displayed and the build will fail

"Invalid character escape" can be displayed: If you pass any paths with white spaces you have to put them into quotation marks. If cmake still complains about wrong escape chars you have to remove all the cache files.

In-Source Build is prohibited

In case this error happens: "CMake Error at CMakeLists.txt:4 (message): In-Source Build is prohibited. Please execute cmake from a different directory". Please make sure that the entire repository is clean in the same state that you cloned it (e.g. use git reset --hard) before trying to build again.

The CMake build can fail because it cannot clone an external git dependency

The CMake build will fail if it cannot download an external dependency. In some cases it fails because it is not able to provide the credentials for this repository because your private key might not be loaded properly. Open a command prompt and try to clone the repository yourself, if this fails or if you have to provide a passphrase, you need to do some changes. CMake does not allow for any user input, so you either have to use an unprotected key or your passphrase needs to be cached. On Windows, you can follow these steps to get it working: https://stackoverflow.com/a/4356869. If you run into an additional Problem with "fatal: ssh variant 'simple' does not support setting port", you can follow these steps "https://stackoverflow.com/a/48417901". Make sure to restart your IDE so that it can pick up the environment variables that you configured.

CMake structure overview

CMake Projects are often structured into libraries and executables. This is (mostly) not the case for FruityMesh however, because as it turned out, splitting the project into libraries increases the hex file size by quite a lot without giving us actual benefits. Due to this we have decided to compile everything and link everything individually per featureset/simulator instance. Each featurset as well as simulator configuration (tester/runner) is an individual executable. To avoid adding all the files to each target by hand, all the targets are stored in several lists. Currently the following lists exist (not all publicly available):

  • ALL_TARGETS - Every target

  • NATIVE_TARGETS - Only targets that are the product of a single feature set. Most of them run on actual hardware, though there are exceptions to this like prod_mesh_arm which is intended only to check that the HAL abstracts all vendor specific code out.

  • SDK_14_TARGETS - Targets for the nrf52832.

  • SDK_15_TARGETS - Targets for the nrf52840.

  • ASSET_TARGETS - All assets, both classical, development, and INS.

  • BP_TARGETS - Belparts targets.

  • CLC_TARGETS - Regent targets.

  • VS_TARGETS - Vossloh targets.

  • WM_TARGETS - Waldmann targets.

  • EINK_TARGETS - Eink targets.

  • VIRTUAL_COM_TARGETS - Targets with virtual com port functionality.

  • ARM_TARGETS - Currently only prod_mesh_arm.

  • SIMULATOR_TARGETS - Only targets that run in the simulator. At time of writing these are cherrySim_tester and cherrySim_runner.

To simplify the work with these lists several macros are defined in CMake/MultiTargetCommands.cmake. Most of them just apply a single function on all targets in a given list.

Each featureset has its own cmake file which is located in fruitymesh/config/featureset/NAME.cmake. The only mandatory job of these is to set the platform of a featureset (commonly the first line in these files). Currently available platforms are: NRF52832, NRF52840, and ARM. In addition to this mandatory step, the featureset cmake files may add additional files and configs. Sets of such files and configs and somehow belong together (e.g. adding the Eink capability) are grouped for reusability in fruitymesh/config/featuresets/CMakeFragments. The featureset cmake file can also set some special variables. These are:

  • set(ALLOW_MALLOC 1) - Allows malloc and new functionality to be compiled in the firmware. CAREFUL: This does NOT mean that malloc and new are allowed to be called! This was required for TensorFlow in the INS featureset. When compiled with a special flag TensorFlow guarantees that malloc/new is not used. However, they still link to it.

  • set(FAIL_ON_SIZE_TOO_BIG 0) - Does not fail compilation if the firmware is too big for updates (but does print a warning). Useful for development featuresets. In addition this feature is currently used for NRF52840 featuresets as a work around.

ONLY_FEATURESET

When generating the project files with CMake for building a featureset, it is possible to set the "ONLY_FEATURESET" parameter. If set, only the given featureset (the one that is stored in this parameter) will be generated. There are some advantages to this, for example the generated Makefile shows the done percentage more clearly. If all featuresets are generated, the shown percentage jump in steps of 20, while if only one featureset it generated, the percentage is smoothly rising from one percentage to the next. Another advantage is better intellisense code hints in visual studio code. For example, it indicates better which areas are currently disabled by an #ifdef.

BUILD_TYPE

The "BUILD_TYPE" parameter can be set to either SIMULATOR or FIRMWARE with the later one being the default. Depending on it either the GNU ARM Embedded Toolchain is used to produce binaries for e.g. the nrf52 boards or the cherrySim_runner and cherrySim_tester executables are built.

SDK, Chipset, BLE Stack Compatibility

We have historically supported nRF SDK 14 for building the firmware for nRF52832_XXAA chipsets and have added support for nRF SDK 15 for building the nRF52840_XXAA targets.

We have now extended this by adding support for the latest nRF SDK 17 (integration is currently in alpha state) which can be used to build the firmware for both nRF52832 with S132 and nRF52840 with S140 SoftDevice. These are the two chipsets that we and our customers are mostly using and that are thoroughly tested by us. You can however easily extend the CMake build to also build the firmware for other chipsets that are supported by the SDK. Be aware that you should test functionality thoroughly, but most things should be working out of the box.

To try out the SDK 17 integration, e.g. add the following to your featureset and make sure to not use the PLATFORM variable anymore.

We have manually added the latest SoftDevice version 7.3.0 to the SDK folder for S132 and S140 as it was the latest update available. SDK 17.1 originally shipped with version 7.2.0.
set(TARGET_SDK NRF_SDK_17_1_0)
set(TARGET_STACK NRF_S140_V7_3_0)
set(TARGET_CHIPSET NRF52840_XXAA)