Is it possible to build binaries for different targets using CMake? - cmake

I'm considering to use CMake for projects targeting a microcontroller. I found out how to create a toolchain file and invoke cmake -DCMAKE_TOOLCHAIN_FILE=Path/To/Toolchain.cmake to make CMake do cross-compiling.
However most projects that I work on have also code that must be compiled for the host platform. These are often unit tests or other test tools, which share most part of their code with the binary that will run on the microcontroller. A rare case might be a project that even has two processors having a different instruction architectures, thus needing a host compiler and two different cross compilers.
I'd like to have one build that rules them all. Is it possible to have a construction that I only need to call cmake /path/to/source && make, or is the only solution having multiple 'root' CMakeList.txt files, each for every target?

Each cmake run will target one specific generator and thus one platform.
What you want can be achieved by having one hierarchy of CMakeLists files for each platform. You need to get to a point where doing a succession of cmake .. && make calls will build the whole project.
Then write a master CMakeLists that executes all of those separate build steps for you, e.g. through ExternalProject_Add or by using custom commands. Depending on the structure of your project it might make sense to have only the tools required for building being processed this way and add the sources for the actual project directly to the master CMakeLists instead.

Related

Generating compile_commands.json without generating build files

I'd like to generate a compile_commands.json file for use with the clangd language server. However, EXPORT_COMPILE_COMMANDS only works for the make and ninja build systems. When building a project that uses a different build system it would be convenient to also be able to generate compile_commands.json files as if I was using make or ninja without actually generating any build files that interfere with the build system that I'm using to perform the build.
What is the most convenient way to do this with cmake?
I think your only option here is to have a different build folder with Ninja or Makefile to generates the compile_commands.json and have a different build folder for your "actual" build.
The thing is, CMake is a generator, and it doesn't support mixed builds; and in fact, it should not. If they do that, you will end up having random artifacts from different build systems inside the build folder that might eventually conflicts with each others.
That being said, you are aware that what you get in Ninja-based compile_commands.json is not going to be fully relevant to your "actual" build system that you want to use. I can see it being useful, but not the same for sure.

CMake mulitple build projects in single tree

we use several git repos and our build system is cmake. We have several libraries with unit tests (seperate executables) and several applications (release) that we are going to build. In the moment we solve this by having multiple workspaces and a proper Top-Level CMakeLists.txt for the purpose of the current workspace (e.g. test of library A, test of library B, release for application X)
Now we would like to use a single tree (iso multiple workspaces) that includes all libraries, test code and applications, which means we are able to build everything from single tree.
In principle the core problem here is that we need to "merge" our multiple existing Top-Level-CMakeLists into this single tree.
This seems on first view rather easy, but the problem starts at the point that we want to build libraries with different flavours based on the same tree (e.g. using different compile flags, linker flags).
I will make a simplfiied example with a common cmake tree:
tree/
CMakeLists.txt
libA/
CMakeLists.txt
libB/
CMakeLists.txt
AppA/
CMakeLists.txt
AppB/
CMakeLists.txt
So lets say, my both applications are using libA, libB - but in case of AppA I would like to use different flags than in AppB.
Somehow I need to tell CMake to compile libA twice and to associate the right version of libA to my corresponding App.
Any ideas or is it in general not possible to have multiple projects in a single tree with CMake?

How to handle autotools project with cmake dependency?

I have an autotools C project that needs to use another library that is built with CMake. Is their an equivalent to AC_CONFIG_SUBDIRS that will work with CMake?
I take it that you want to configure and build the CMake-based project as part of configuring and building the Autotools-based host project. This is possible, and there are several viable ways to do it, but I'm not aware of anything wholly pre-packaged like AC_CONFIG_SUBDIRS is for Autotools-based subprojects.
For configuration
Option 1 - config commands
Autoconf provides a group of macros by which you can specify custom commands for configure or the generated config.status script to run. You could use one of these -- probably AC_CONFIG_COMMANDS, but maybe AC_CONFIG_COMMANDS_POST -- to run cmake (and any wanted preparatory steps) in the subproject. Personally, I like this option best.
Option 2 - glue script
AC_CONFIG_SUBDIRS instructs configure to run configure scripts in the specified subsirectories, but those other configure scripts don't need to be Autotools-generated. You could conceivably write a custom wrapper script named "configure" in the subproject directory for the parent configure to run, but which itself performs an appropriate call to cmake. AC_CONFIG_SUBDIRS in the top-level configuration should then run that script at the right time.
Option 3 - custom code
I think Autoconf already provides sufficient support for what you seem to want, but if you think otherwise then you always have the option of writing whatever shell code you want into configure via configure.ac. You might find it worthwhile to write a custom macro for that, especially if you have multiple CMake subprojects, but that's not obligatory. Note that such commands are distinguished from those specified via AC_CONFIG_COMMANDS & co. by the timing of their execution.
For building
Presumably you'll be relying on recursive make during the build and installation steps. It shouldn't be hard to make that work, whether you're using an Automake-based Makefile.in or a hand-rolled one at the top level.
Option 1 - Automake + glue makefile
Use a SUBDIRS variable in your top-level Makefile.am to direct make to recurse into the CMake project's subdirectory, just as you would do into any other project's. Write a simple Makefile there that recurses into a build subdirectory (which you will have had to ensure is created and configured by configure). This should not collide with the subproject because it presupposes that a separate build directory is used. The glue makefile can adapt targets and make variables to the expectations of the subproject's build system.
The Automake documentation describes all the recursive targets that the top-level Autotools makefile might try to build recursively, and the glue makefile should provide all of them -- though there may be many that need only a dummy (but not empty) recipe.
Option 2 - hand-rolled top-level Makefile.in
If, on the other hand, you're using a hand-rolled top-level Makefile template then you have full control over your recursive make invocations. You could still use a glue makefile in the subproject in this case, but it's probably easier and cleaner to just adapt directly to the expected CMake-generated makefile.

Compile gtest from source with catkin

I am trying to compile gtest from source (instead of using the existing installed version). I am working on a catkin based cmake project.
I have added the sourcecode from https://github.com/google/googletest to my workspace and included the folder with add_subdirectory.
However, I get a nameclash with the existing gtest:
CMake Error at src/test_env/GTest/googletest/cmake/internal_utils.cmake:151 (add_library):
add_library cannot create target "gtest" because another target with the
same name already exists. The existing target is a shared library created
in source directory "/usr/src/gtest". See documentation for policy CMP0002
for more details.
From other posts, and the googletest instructions itself (https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project) I understand that this should be no problem.
I think the problem might lie in how catkin handles gtest. And, admittedly, normally I could just use the installed version. But I want to make sure, that everyone uses the same (bundled) version of gtest.
Any suggestions and hints are welcomed.
Okay, so the error message is actually quite clear. A cmake "target" is "something that will be produced by the build", be it a library, or an executable, or something else. So, the problem is that you are trying to add a target named "gtest", and catkin already does the same thing. Both would produce the library "libgtest.so", and of course there can only be one of those in the same folder. You could rename "your" gtest by changing the target name in googletest/CMakelists.txt, but I would strongly advise you to not do that.
In my opinion, gtest shouldn't even be a shared library at all, especially if you are using different build flags for different projects in your repository. There is an alternative, and that is basically only including the gtest source code in a folder, and then including the header files and source files in your unittests main.cpp. googletest already comes with helpers for that, that is src/gtest-main.cc.
This is how I would structure it:
Add the gtest version you want as submodule to git (in case you use git). This way, you have a specified version for all projects in your repo, and can update it in a different branch. I will call that folder "GTEST_DIR".
Write your unittests in .cpp files, that #include <gtest/gtest.h>, one per hpp you want to test, and #include both the hpp and the cpp in your test.cpp. This enforces the separation of your tests from other classes and makes it very easy to switch out dependent classes with mocks or fake objects. You will not need a main() function, as that one is already in gtest-main.cc.
Write a cmake macro like this:
macro(add_gtest NAME FILES)
add_executable(my_gtest_$NAME
$FILES
GTEST_DIR/src/gtest.cc
GTEST_DIR/src/gtest-death-test.cc
GTEST_DIR/src/gtest-filepath.cc
GTEST_DIR/src/gtest-port.cc
GTEST_DIR/src/gtest-printers.cc
GTEST_DIR/src/gtest-test-part.cc
GTEST_DIR/src/gtest-typed-test.cc
GTEST_DIR/src/gtest-main.cc
)
target_include_directories(my_gtest_$NAME GTEST_DIR/include)
endmacro()
Of course, you can make this more complicated or less complicated, but that is the gist. Of course, compile times will be longer this way over using gtest as a shared library, but it actually makes sure your units get tested in isolation, which is very valueable in my opinion. Also, you can use ccache to greatly improve compile times in this scenario, because the gtest object files never change. Also, this will make sure gtest is compiled with exactly the flags you want it to. You could for example create 2 separate unit tests for the same class, one with exceptions enabled and one without.

CMake: automatically find target dependencies in other CMake projects

If we have a case of highly decentralized development environment, where there are many repositories and projects, is there an existing functionality in CMake that automatically finds dependencies between targets without a top level CMake file?
The workflow is something like this, you specify a directory and all targets are default-configured in the given tree. Then you can go and build any of the projects. I am looking for a behavior similar to that when you build the Android OS.
There is no build-time dependency tracking in CMake across different projects. For this case you need to have a project on the top-level which adds all the subdirectories, so that the target names are available inside a single CMake project.
I am aware of one helper script around CMake which provides the required inter-project dependencies: https://github.com/aldebaran/qibuild
I would say that is getting close to a mature code base. However, it requires additional descriptor files for each project. Might be worth to have a look at it.