A Triumvirate is or Triarchy is built by three individuals which lead or rule something. In this article I want to rule a project with Eclipse CDT, Visual Studio Code and with building it from the command line for automated builds.
So what if I have an Eclipse project (say MCUXpresso IDE and SDK), and want to build it on a build server, and and I want to use the same time the project with Eclipse IDE and Visual Studio code?
Key to this is CMake: I’m keeping the Eclipse CDT features, adding CMake with Make and Ninja to the fix, and have it ‘ruled’ by three different ’emperor’: Eclipse, Visual Studio Code and from a shell console:

Outline
Eclipse CDT managed build projects greatly simplify project handling from a user perspective: create a project, add or remove source files and hit ‘build’, and everything is taken care of. But this depends on the build plugins used. It makes porting harder to port from one target platform to another, because different vendors have different plugins, with incompatible settings. That’s fine in some environments, but as soon more complex things like remote build servers, independent command-line building or just more control over the build process is needed, then the CDT managed build process is not really scalable. CMake is the solution to that problem.
In an earlier article “Tutorial: Creating Bare-bare Embedded Projects with CMake, with Eclipse included” I showed an approach how to create your own CMake project from scratch. In this article I show how an existing MCUXpresso project can be extended with CMake, so I still can use the Eclipse way of building, but the same time I can use CMake and to build build it with make or ninja. As ‘icing on the cake’, I get it working with Visual Studio Code. I have freedom of choice: what IDE I want to use with my build system of choice.
The sources of the project discussed in this article can be found on GitHub.
Approach
In order to have CMake working, I need CMake with Make and/or Ninja installed and present in the path. I’m using the following tools and versions:
- MCUXpresso IDE V11.7.x with its internal toolchain
- MCUXpresso SDK 2.13.0
- CMake 3.22.5
- Ninja 1.11.1
- GNU Make 4.3.90
- Visual Studio Code 1.77.3
To use CMake with an existing Eclipse project, CMakeLists.txt files need to be added to the project, a toolchain defined (arm-none-eabi-gcc.cmake) and the linker files provided (copied from the auto-linker files). Additionally I have added custom build targets plus some batch files to make setup easier.

SDK CMakeLists.txt
Place a CMakeLists.txt
into each of the SDK folders. They include all source files of the folder and create a library.

Below is the example for the ‘drivers
‘ SDK folder:
file(GLOB FILES
*.c
)
# add_library: With this declaration, you express the intent to build a library.
# The first argument, here its pico-shift-register, is the name of the library,
# the second argument are the files that will be compiled to create your library.
add_library(drivers ${FILES})
# target_link_libraries: If you link with other libraries, list them here
target_link_libraries(
drivers
)
# target_include_directories: Libraries need to publish their header files
# so that you can import them in source code. This statement expresses where to find the files
# - typically in an include directory of your projects.
target_include_directories(
drivers
PUBLIC
./
../device
../CMSIS
)
The ones for ‘board’, ‘component’, ‘device’ and ‘utilities’ are very similar.
Toolchain File
To define the toolchain, add the arm-none-eabi-gcc.cmake
file:

Below it uses the toolchain of the IDE 11.7.0:
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR ARM)
# point ARM_TOOLCHAIN_BIN_DIR to things like
# "C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2020-q4-major/bin")
# "C:/NXP/MCUXpressoIDE_11.7.0_9198/ide/tools/bin")
set(ARM_TOOLCHAIN_DIR ${ARM_TOOLCHAIN_BIN_DIR})
set(BINUTILS_PATH ${ARM_TOOLCHAIN_DIR})
set(TOOLCHAIN_PREFIX ${ARM_TOOLCHAIN_DIR}/arm-none-eabi-)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(CMAKE_C_COMPILER "${TOOLCHAIN_PREFIX}gcc")
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_PREFIX}g++")
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy CACHE INTERNAL "objcopy tool")
set(CMAKE_SIZE_UTIL ${TOOLCHAIN_PREFIX}size CACHE INTERNAL "size tool")
set(CMAKE_FIND_ROOT_PATH ${BINUTILS_PATH})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Linker Files
Create a folder named ‘ld
‘ for the linker files and copy the linker files from the ‘debug
‘ (generated by the linker) to the new folder:

In case your SDK is not using generated linker files, you can simply use the one provided.
Main CMakeLists.txt
Finally, add the main CMakeLists.txt
to the project. You can use the one from GitHub. In that file, we reference and link the SDK libraries we have created earlier:
add_subdirectory(./board build/board)
add_subdirectory(./drivers build/drivers)
add_subdirectory(./utilities build/utilities)
add_subdirectory(./component build/component)
target_link_libraries(
${EXECUTABLE}
board
drivers
utilities
component
)
If your project has a different set of folders, simply extend/change the above structure.
Generating Build Files
Using CMake means that CMake has first to run to create the Make or Ninja files. In other words: CMake is a ‘build generator’.
One challenge with CMake is that by default it ‘clutters’ the source folders with the generated files. What I prefer is to have everything generated in a ‘build’ folder, so I can easily get rid of it (clean). The ‘magic’ option for making this possible is the -B option:
cmake -G"Unix Makefiles" . -B build
or
cmake -G"Ninja" . -B build

I recommend using Ninja, as it will build around 10x times faster!
I find the -B
option very useful, as that way I can execute CMake from the project root directory, otherwise I would have to do it the following way:
mkdir build cd build cmake -G"Ninja" ..
This is of course doable, but why not making everything in a single step?
The next ‘magic’ option to be used with make
or ninja
is -C: this makes a ‘change directory’ before executing the build. To start the build from a console in the project root directory, I can use
make -C build
ninja -C build

Build Targets
To make building with CMake easier, I have created Build Targets:

For Make it looks like this:

You might notice the ../build
: this is needed because Eclipse uses the ‘build output’ folder as ‘current directory’, so I need to step up one directory (..
) and then down into the build
folder.
Very similar the same thing for Ninja:

Using the Build targets I can easily start a build from the Eclipse GUI.
Make or Ninja?
I grew up with Make, and I feel comfortable with it. But for larger projects I recommend ninja: it is optimized for speed, in the range of 5-10x faster than make. For the project in this article, a full build with make takes 5 seconds, where a full build with ninja only takes 1 second.
You can measure the time for example with:
powershell ("Measure-Command {make -C build | Out-Default}").TotalSeconds
or:
powershell ("Measure-Command {ninja -C build | Out-Default}").TotalSeconds
For larger projects, the speed factor is in the 10x range.
Visual Studio Code
With having the project based on CMake, it is now open to be used with Visual Studio Code too. All what we need is to add a toolkit, as described in Visual Studio Code for C/C++ with ARM Cortex-M: Part 5 – ToolKit: with this we can use the same project with CMake, Eclipse and Visual Studio Code:

Finally, I have something I can use with Eclipse, Visual Studio Code and on the command line:

Summary
Adding CMake capabilities to an existing Eclipse CDT based project is not very difficult: it requires adding the needed CMakeLists.txt
file to the project plus a tool-chain definition file. With CMake I can not only enjoy full control over the build process (both from the IDE and command line), while keeping all the IDE features and capabilities, like configuration tools or debugging features. I can do the build both from the IDE and on the command line too. And it allows me to use a ninja based build, which is amazingly fast. Plus I can use the same project with Visual Studio Code.
Happy ruling 🙂
Links
- Project on GitHub: https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/MCUXpresso/FRDM-K22F/FRDM-K22F_SDK_CMake
- Ninja: https://ninja-build.org/
- Building Eclipse and MCUXpresso IDE Projects from the Command Line
- Visual Studio Code Series: Visual Studio Code for C/C++ with ARM Cortex-M: Part 1 – Installation
Using absolute paths in the toolchain file makes the project non-portable, not to mention the Windows specific details.
LikeLike
That has been changed. That was only for some tests, and I had not pushed the latest files. It is using a variable now.
LikeLike
Great! If you fix the .exe too, your project will also run on non-Windows platforms.
LikeLike
Ah right! Fixed now. Multumesc!
LikeLike
Wow, the master has arrived. Thank you so much for your work on Eclipse Embedded CDT and xPack.
LikeLike
Thank you Yu for using my projects! Did you try the xPack extension for VS Code? It allows to further automate dependencies to binary tools like cmake, ninja, toolchain, etc and also to define sequences of commands (like those you mentioned in the next comment) that you can later invoke with the click of a mouse.
LikeLike
Erich, thank you for sharing your insights. After reading your post about using VSCode and CMake, I decided to migrate from Eclipse to VSCode. The VSCode community is much more active than Eclipse, and the Github Copilot extension has proven to be a powerful tool. Additionally, I integrated Cppcheck and Unity testing framework into the CMake build process for code checking and unit testing. With the help of pipelines, the entire building and testing process is now automated on the server before the code is merged into the main branch. Furthermore, with the use of CMake presets and workflows, the building process has been further simplified. Here are some helpful commands for CMake:
cmake –build build (This command calls ninja/make to build the project.)
cmake –workflow –preset test (This command configures, builds, and tests the unit tests in one go.)
cmake –workflow –preset debug (This command configures and builds the project in one go.)
LikeLike
We migrated also some months ago from Eclipse (STM32CubeIDE).
For the environment we use vcpkg, which downloads all for the environment (gcc, cmake, ninja, …)
There is a VSCode extension “Embedded Tools” from Microsoft which has vcpkg integrated.
A new User/Programmer needs only to install VSCode and checkout the project from git.
With the help of “extension.json” the needed extension are shown to install.
After that vcpkg downloads all (gcc, cmake, ninja, …) and then he is ready to work.
For our gitlab runner we use the standalone vcpkg.
Its really smart if you have different version of gcc, cmake,.. for different projects.
We are really happy to remove Eclipse.
LikeLike
I’m using msys2, which uses pacman to manage packages. I did not try vcpkg yet, thank you for your sharing.
LikeLike
I have tried vcpkg shortly after it came out, but it it did not work at that time. Maybe if I find time I could try it out again, as it might have improved by now. Personally, I feel the xPack approach is better and actually it works. But if MS is pushing something, it does not mean the better thing will be broadly used …
LikeLike
Funny… I’m always falling back again to Eclipse after several not so successful attempts to do it with VSCode. But perhaps that is some kind of personal taste.
Concerning Erichs post: I’m using additionally a “stub” Makefile for Eclipse integration which does substantially “ninja -C $(BUILD_DIR) -v all” for the build target. But there are other targets which control the configuration of the build process. But the Makefile is really small and is portable between Linux/Windows platforms.
That’s my current solution to integrate cmake into Eclipse.
LikeLike
That’s a very good idea with that ‘stub’ make file, thanks for sharing!
LikeLike
Hi Alex,
I have many different IDEs installed and in use, basically a mixture of VSC, VS and different Eclipse distributions. I had tried to get vcpkg up and running, but that did not work some months after it has been released, so I might give it a new try.
LikeLike
This is a very interesting idea. I usually end up having both VSCode and MCUxpresso open. When I want to build or debug I tab over to MCUxpresso.
Have you experimented with VSCode devcontainers at all? The way our corporate antivirus is set up it is actually faster to run my unit tests and static analysis in a container. Plus then I can use the same container image on our build server.
I look forward to someday getting my projects all set up so I can debug in VSCode, running in a devcontainer. Then we can be sure everyone on the team has the same environment.
LikeLike
On the contrary, we prefer use different environments inside our team, so that some problems could be found at early stage. However, we use an identical environment to build the hex for the final production.
LikeLiked by 1 person
same for us here: we are using different environments and tools, but consolidate on the build process to produce the binaries. The editor or IDE topic seems to be more of a ‘personal taste’ thing and in forums it easily ends up in ‘editor wars’, starting with vi vs emacs vs nano vs xyz ;-).
To me, each environment has its own benefits, and not a single tool will and can do everything for every case. Key to me is diversity and freedom of choice to get the job done.
LikeLike
I know it could appear a dumb question, but why do you use CMake and not a simple Makefile?
For what I understood, CMake is a good thing for cross-platform build process, because it automatically generates a correct Makefile for the current OS (I think it’s possible to create a cross-platform Makefile, but it is not simple). However, if my team uses only Windows, I think I could use a simple Makefile to obtain the same thing you did: running the build process from Eclipse CDT, Visual Studio Code and command line.
LikeLiked by 1 person
One major benefit of using CMake/Ninja is that it can speed up the building process by around 30% compared to Makefile. I don’t know the reason. The finnal building commands seem the same, but Ninja just builds faster.
Cross-platform is also an advantage. In my team, me and some colleagues use Windows, other colleagues use Mac, and the gitlab pipeline is based on a Linux docker image.
Though I think CMake is more suitable for complex projects, I agree that there’s room for debate on whether CMake or Makefile is better/easier.
LikeLiked by 1 person
About why Ninja is (or could be) faster: In a simplistic way, you can see Make as a ‘compiler’ and Ninja as an ‘assembler’, with CMake as the ‘build file source generator’. Make needs to parse the make source files, build up the internal data structure and dependencies and then perform the build. In contrast, the Ninja parser is simpler, and can translate the build actions much more direct.
Another view is to see Make files as script files (interpreted), and to see Ninja files as compiled files (executed).
LikeLike
Another small details: by design, the build.ninja files are expected to be generated by automated tools (although it is perfectly possible to write them by hand), while the make files, also by design, are expected to be written by humans. This translates into an added complexity for the make files. Another difference is the approach to project configurability. In addition to the build itself, make tries to provide support for configurable builds. At first sight this may look like a nice feature, but at a closer look it turns out to be expensive, since it increases the make files complexity and the computational requirements to process the additional semantics. Running the same complex logic at each build, even if nothing changed in the project configuration compared to previous builds, is wasteful. In contrast, ninja delegates the configuration features to the upper level (the build system generator) which runs only when the configuration changes, and all subsequent builds use the same (simpler) build.ninja files, which need less decisions to be taken at build time.
—
I studied build systems quite extensively, and my library projects provide direct support for integration in both CMake and meson projects. This solved the multi-platform requirements and provides a way to integrate the builds into scriptable environments, like Continuous Integration/Delivery. Unfortunately the very convenient way of editing the project configurations via a GUI tool (like Eclipse) is lost, since CMake/meson configuration files were designed to be edited by humans, not by automated tools.
The solution that I’m considering is a new tool (tentatively named The xPack Project Builder) which will provide the basic CMake functionality and generate ninja files, but store the project configuration in JSON, a format that can also be edited via a set of configuration pages in a GUI tool (like the C/C++ Build -> Settings in Eclipse). This would allow VS Code to provide a similar functionality as the Eclipse CDT managed build.
LikeLiked by 1 person
Excellent analysis! IMHO one of the success factors of Eclipse is that easy project configuration in a GUI tool. CMake provides a kind of GUI tool, but it is pretty useless in my view. While using CMake with VSC is not a bad thing, it is the biggest obstacle of using VSC especially for ‘new’ users, as I can see with all my classes using VSC. With Eclipse, project setup and configuration is very intuitive, even from scratch, while with VSC without the proper CMake file provided it is very hard, because you get all the external tool dependencies into the mix.
Having something like a ‘xPack Project Builder’ would be great!
LikeLike
> you get all the external tool dependencies into the mix.
This is already covered by the existing xPack functionality, the external tool dependencies are listed in the package.json file, and are automatically installed, with exactly the desired versions for each project, by the ‘xpm’ tool.
You can see functional projects using this configuration by instantiating the latest project templates added to Eclipse Embed CDT (the Arm & RISC-V xPack C/C++), or you can manually instantiate a project in a terminal with the ‘Hello World QEMU’ template (https://github.com/micro-os-plus/hello-world-qemu-template-xpack/).
Automating dependency management might save you and your students some time.
LikeLike
> Having something like a ‘xPack Project Builder’ would be great!
A preliminary prototyping of the configuration files used by this tool can be previewed at https://github.com/micro-os-plus/startup-xpack/blob/xpack/xpack.json.
The main concept here is that the project can be split into separate ‘components’ (one or more files that enter the build together) and ‘options’ (configuration choices that translate into program definitions, like macros).
The GUI (like VS Code) should display the tree of these nodes (components & options) and allow the user to configure the source files, the include files and compiler options for each component.
Based on the dependencies between such components, the xPack Builder tool should decide which sources enter the build and generate the build.ninja tile with the compile/link commands.
LikeLike
It is not a dumb question, in fact the distinction is quite deep actually. CMake is a build system generator (as meson is too), while make is a build system (as ninja is too). The output of a build system generator is a set of configuration files for the build generator. Thus CMake/meson run at a higher level of abstraction; you define WHAT you want to be done and the build system generator figures out what tools to invoke, while for make/ninja you have to define HOW you want it done in detail. Although maintaining CMake/ninja configuration files is not as easy as it should be, manually maintaining make files for complex projects is way more difficult. For those who used only Eclipse for their projects, the difficulty of writing make files is not obvious, and we should not downplay the merits of Eclipse CDT for doing it auto-magically for us.
LikeLiked by 1 person
“The output of a build system generator is a set of configuration files for the build system.”
LikeLiked by 1 person
I have a little suggestion about your new tool.
Before adopting CMake, I have compared many other similar tools, such as Meason and Ceedling. I searched forums and read numerous posts to decide which one to use. On one hand, newer tools like Meason have many advantages. On the other hand, people hate CMake’s language. But many still choose to use CMake for one main reason: it is more widely used. This is also why I choose to use CMake.
Wider usage means more existing tools, fewer bugs, and better community support. Additionally, CMake can be easily installed through pacman/apt/yum/brew. Moreover, since CMake is widely used, my teammates are more likely to already know how to use it. Even if they don’t, it would be much easier to convince them to learn CMake.
Introducing new tools can be exciting, but it can also be challenging to scale their usage. Unless there’s a significant advantage, people tend to stick with the widely used tools. Therefore, I recommend extending existing tools like CMake/Meason instead of developing a completely new tool.
LikeLike
Liviu worded it very well in his answer. To add to this:
For very simple things (a few files), a script or a batch file works well.
For going a bit more complex, make is a good choice: it manages dependencies, uses targets, and so on. In principle, you can do everything with make.
Make has been around for a very long time, and has been used a lot (still is used a lot).
Make as descriptive language requires knowledge like of any other programming language, and can be daunting. This is why Eclipse for example makes it so easy to add files to a project, change options, etc: it is very easy.
But it gets difficult if you want to build it outside Eclipse, or if you want to use the generated make files on different hosts (Windows, Linux, …), as tools and paths are different.
CMake aims to solve this, as it adds another layer on top of the build (or make) files: CMake as yet-another-build-description-language has different generators, for example to build make or ninja files, taking the platform specific things into consideration. The other benefit is that one can easily change underlying build system from make to ninja, with the benefit of speed. Make has grown over the years (like Eclipse did), where ninja has been built up from the ground with speed in mind.
In essence: you can do everything you want with Make, but CMake adds another tool to the mix which gives extra benefits. I would compare CMake/Ninja vs Make like Eclipse vs. Visual Studio Code: both can do the job, each with its own pros and cons. But with having CMake on top of everything, you can freely move between everything. Well, you still need to master the dungeons of CMake 😉
LikeLike