evaluating cmake

I am evaluating if cmake can help me in my projects.

First of all I should make a premise about the constraints we have in the build processes.
We build products based on microcontrollers running baremetal or RTOS (FreeRTOS) firmware.
The products are getting more and more complex. They use more microcontrollers, and FWs are encrypted. The FWs themselves are getting more and more complex, a few hundred files, dozens of directories, use of third-party libraries and middleware, etc…
At the moment we are using make, but makefiles become less and less maintainable as projects get more complicated.

We produce executable files in all formats: .elf for debugging, .bin, .ihex, etc. for the production department. We also produce some compendium files like .map, .lss, .sym, etc.

We adopt a semantic versioning (https://semver.org/) scheme. The metadata field is populated with commit tag of git (command git describe --always --dirty --broken --exclude=*).

All files that we produce must also contain the version in the name, for example:
product_v1.2.3-alpha.1+3e4d243-dirty.elf
or
product_v1.2.3+a45cb2f.map
Symlinks must be created whose names must not contain the version (for debugging purposes):
product.bin -> product_v1.2.4-rc.2+f599a72.bin

We adopt the out-of-tree build: all files (finished products and semi-finished products) must reside in a dedicated directory. In particular we also split final images from semi-finished products. This also applies to the generated code that will never “mix” with the handwritten code.

The FW must know its version. The solution found at the moment is to have a target in the makefile that writes a header file containing all the information about the version. This file is not rewritten if the version is not changed (including metadata). This header file is included from a single .c (this is for dependency issues).

I would like to try to illustrate our flow with some schemes.

Here is the basic build flow. Starting from input files we get a plain executable.
plain_binary_build
I would like to point out that the .lst should reside together with the .o and the .d.

Then I use the previous macro to build the encrypted binary:
encrypted_binary_build

Once I have all my binary encrypted I can bundle them in a single deploy package:
encrypted_deploy_image_V

In any case I might want to pack the plain versions (for debugging needs):
plain_deploy_image_V

A further requirement is that it must always be possible to specify one of the semi-finished products as target. For example it must be possible to specify the FW2 plain or the FW3 encrypted and these must be built without creating the deployment package.

Having said this long premise I would like to understand (with you) if CMake can be used in such a context. Is it possible for CMake to generate a set of makefiles or ninja that respect the above constraints and behave similar (or identical) to our current build flow?

best regards
Max

I don’t know how much control over the linker step CMake offers. I imagine a toolchain could be constructed, though the multi-output would seem to be the stickier part right now (you’d have to choose one as the “main” output and it’s the only one the build would “see” right now). Though maybe the .dll/.lib split offers a mechanism for that kind of thing?

Postprocessing the executables with encryption should be possible with an add_custom_command.

For extracting git information at build time, I’ve done it before here though it is dependent on the sprokit_configure_file which isn’t complicated, but it is tricky to get right.

Embedding version information into source is relatively simple, though putting it into the executable name is probably harder (as the output could change behind CMake’s back). Personally, I’d consider the install and/or packaging step to take the version info and rename binaries at that point and then make the symlink.

CPack could be used at the packaging level via a top-level ExternalProject setup to combine the various firmware projects together into a single package.

I’m sure there are actual details that matter as well, but I don’t see any fundamental reason why CMake wouldn’t work.

Cc: @craig.scott @brad.king

For this point I found another solution, for which I made another post: express the right dependencies in CMake
There are attached files, so you can evaluate if it is a “valid” solution (and maybe give me an answer if you know it :sweat_smile:)

I will analyze and try the solution that you propose in the next few days.

best regards
Max