I have two CMake application projects, with libraries they share in Git submodules. I include the libraries in the CMake applications using add_subdirectory() and target_link_libraries().
One application uses an RTOS; the other doesn’t (it’s “bare metal”). Some of the shared libraries need to know whether they are building for use with or without the RTOS, and adjust their target_sources() / target_compile_definitions() / target_link_libraries() accordingly. What is the proper modern CMake-esque way to do this?
Currently, the application w/ the RTOS does option(use_rtos "Build libraries with RTOS support" ON)
in the application CMakeLists.txt. This seems like a bad approach, since it sets a cache variable, which (if I understand correctly) is supposed to be override-able by the “user” (application developer at build time, without editing CMakeLists.txt).
I imagine I’d want to set() and check a “normal” (non-cache) variable, but there’s no bool type AFACT.
I think the option is perfectly what you need.
It defines a variable that you can use in your CMakeLists.txt to define the different behaviours your project needs.
The developer can then set the use_rtos to ON or OFF on either the command line, or in his/her IDE. If they don’t provide any value the default given in the option is set. They should not change the cmakelists.txt. In the contrary, that won’t even work because the first time cmake runs it will cache the option and changing it then is only picked up if you clear the cache (but clearing is not necessary if the variable is set from command line or toolchain file).
I believe what you also need are CMakePresets. There you can define the two builds all the configurations/variables that go along with it. The CMakePresets can be used to set the option depending on the preset. A toolchain file can be used to define certain paths differently.
The combination of toolchain and presets should help you direct the configuration phase of cmake appropriately.
As for you question on the cmake way for handling different platforms: Presets is the general answer and toolchains if your are cross compiling.
PS, See https://cmake.org/cmake/help/latest/command/if.html#constant for all the variables that are considered as ‘true’ (wether used from option or from set. I think in this case you need to consider CMake as a soft typed language. the ‘BOOL’ in set(var val CACHE BOOL) is only a hint for cmake-gui and ccmake and not actually used in cmake itself.
Let me clarify: I have two applications, let’s call them app_rtos and app_bare_metal. Both of them have lib1 as a Git submodule, so they each get their own copy of the library source. use_rtos is (and should be) defined in each application’s CMakeLists.txt, such that it is visible to both the application’s and lib1’s CMake configurations.
Ah, this is most helpful, thanks. I think I’ll set(use_rtos TRUE) in app_rtos/CMakeLists.txt, and set(use_rtos FALSE) in app_bare_metal/CMakeLists.txt. Reasoning explained below.
This is part of what was throwing me off. It appears that there is more robust typing for the cache vars than normal vars.
use_rtos should actually not be changeable in these ways, within either application.
I am in fact using CMakePresets and toolchain.cmake (gcc arm none eabi). But again, that is IMO undesirable flexibility in this instance. use_rtos should not be in CMakePresets.json, since “the developer” should not be changing it within the application, regardless of the selected preset.
It seems like you have in fact 3 applications/libraries (The cmake word is ‘targets’).
you have the library, which has the option to use rtos or not.
And then two targets that depend on the library that each need to set this option. Yes, in this case I would also set the use_rtos inside the cmakelists.txt file just before adding the imported library.
Inside the library you can set something like:
if(NOT DEFINED use_rtos)
message(FATAL_ERROR "you need to set use_rtos")
endif()
alternative is to use this option I told you about before and give a default value. The reason for these things is to keep it flexible and error proof. Imagine if tomorrow you need to add a third application and don’t remember anymore that you need this variable. So it either needs to be documented well, or enforced from the code.
Unless of course, you would have the option to deduce it from the target information or the compilers ID’s or something. That could be even more robust, then nobody needs to set the variable, but instead you deduce it during the config phase based on the available target information. Of course you can only go this route if that information is easily available.
A few more hints: depending on the size of your project and your purpose for the library, it might be a good idea to add like a namespace or prefix before the name: lib1_use_rtos. Just to avoid potential clashes with other libraries in the future. This depends a bit wether you see the use_rtos as an option of the library or if you see it as a global setting for all targets.
Another thing: if you have the time and effort you can also use cmake fetchcontent instead of gitmodule. but that requires a bit more work as you will also then need to make the library consumable by other cmake scripts.
But overall, it feels like your question has been answered?