cmake if else with option - if-statement

I have a problem using option together with if-else statement in cmake.
project(test)
option(TESTE "isso é um teste" OFF)
if(TESTE)
message("true")
else()
message("false")
endif()
add_executable(test main.cpp)
It always displays true even if I put OFF in the options, what am I doing wrong?

That's because the value of the option is stored in the cache (CMakeCache.txt).
If you change the default value in the CMakeLists but the actual value is already stored in the cache, it will just load the value from the cache.
So to test the logic in your CMakeLists, delete the cache each time before re-running CMake.

I had a similar problem and was able to solve it using a slightly different approach.
I needed some compilation flags to be added in case cmake was invoked with an option from the command line (i.e cmake -DUSE_MY_LIB=ON).
If the option was missing in the cmake invocation I wanted to go back to default case which was turning the option off.
I ran into the same issues, where the value for this option was being cached between invocations:
cmake -DUSE_MY_LIB=ON .. #invokes cmake and puts USE_MY_LIB=ON in CMake's cache.
cmake .. #invokes cmake with the cached option ON, instead of OFF
The solution I found was clearing the option from within CMakeLists.txt after the option was used:
option(USE_MY_LIB "Use MY_LIB instead of THEIR_LIB" OFF) #OFF by default
if(USE_MY_LIB)
#add some compilation flags
else()
#add some other compilation flags
endif(USE_MY_LIB)
unset(USE_MY_LIB CACHE) # <---- this is the important!!
Note:
The unset option is available since cmake v3.0.2

Try this, it works for me
unset(USE_MY_LIB CACHE)

Related

CMake command line define macro without value

I have the following line in my CMakeLists.txt
add_compile_definitions(DEBUG=$(DEBUG))
so when I compile my code with Makefile, I can do this
make DEBUG=1
But what I really want is to just define the DEBUG macro without setting any value to it.
Is there a way I can do this on a command line with cmake?
With CMake you can, at configuration time, add some CMake variables. For example you can do this cmake -S <src_folder> -B <build_folder> -DDEBUG=ON. This way you will have access to the variable DEBUG in your CMake.
In your CMake you will have this code
if(DEBUG)
add_compile_definition(DEBUG)
endif()
(Note that instead of add_compile_definitions, it is recommended to use target_compile_definitions which will set your DEBUG macro only for one target and not globally to your project.
Example:
add_executable(my_target main.cpp)
target_compile_definition(my_target PRIVATE DEBUG)
PRIVATE means that this compile_definition will only be used by the target my_target and will not be propagated to others.)
But if you're only concern of the building type, I suggest that you use CMake variables that are already present within CMake. You can use CMAKE_BUILD_TYPE which will contains Debug, Release or whatever depending on what type of build you want. Your code will now be this
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_compile_definition(DEBUG)
endif()
And you can use this command line cmake -S <src_folder> -B <build_folder> -DCMAKE_BUILD_TYPE=Debug
And here the documentation https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
Note that this solution will only works for Mono-config generators like Makefile or Ninja and will not works with Visual Studio or Xcode

How to bump the C++ standard from the CMake command line?

Currently I have a project that needs C++17, therefore in the CMakeLists.txt I have this line pretty early on:
set(CMAKE_CXX_STANDARD 17)
From the command line (cmake) once in a while I want to test that the project also compiles with C++20. (to avoid surprises).
How can I choose to compile with C++20 from command line?
If I do cmake -DCMAKE_CXX_STANDARD=20 then it is later overwritten by the configuration instead of 17 being interpreted as just a minimum requirement.
I could check if the variable is predefined to avoid overwritting but I was looking for a more declative way to specify this.
(I am using cmake around 3.18.)
The solution is to remove that set command and use target properties instead:
# set(CMAKE_CXX_STANDARD 17)
target_compile_features(myexecutable PUBLIC cxx_std_17)
Then, setting -DCMAKE_CXX_STANDARD=20 on the terminal should work again.
Make CMAKE_CXX_STANDARD a cache variable. This allows you to easily specify a default that can be overwritten via the command line. Note that you need to make sure no "normal" CMAKE_CXX_STANDARD variable is available, since this value would take precedence over the cache variable.
set(CMAKE_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for this project")
add_library(...)
Using cmake -D CMAKE_CXX_STANDARD=20 binary_dir should allow you to update the standard now, cmake -U CMAKE_CXX_STANDARD binary_dir should revert back to the default.
Alternatively you could create a custom cache variable. This would make a reuse easier, since it would allow you to overwrite the property, even if someone else sets CMAKE_CXX_STANDARD before using add_subdirectory to add your project.
set(MYPROJECT_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for myproject")
# overwrite possibly preexisting value for this directory and subdirectories
set(CMAKE_CXX_STANDARD ${MYPROJECT_CXX_STANDARD})
add_library(...)

CMake MSVC variable gets populated only after call to project

I want to create my project name per my compiler (different names for MSVC or GNU).
I have the following code:
if(MSVC)
project(Block3Windows ...)
else()
project(Block3Linux ..)
endif()
These are the first commands in my CMakeLists.txt file. However, it always enters the else block.
It seems like the MSVC variable gets populated only after the call to project, to test it I've wrote the following:
if(MSVC)
message(MSVC)
endif()
message(MSVC)
project(Block3Windows ...)
message("After Project")
message(MSVC)
I get the following printed:
First run:
After Project
MSVC
Second and later runs (with cache):
MSVC
After Project
MSVC
Why the MSVC from withing the first condition is never being printed ?
This behavior is not mentioned in the MSVC documentation
Am I missing something? How do I create a project name per logic on my compiler environment?
I'm using CMake 3.17.2
What about just a generic initial call to project, and then choose the name with the second call to project.
project(Block3)
if(MSVC)
project(Block3Windows)
else()
project(Block3Linux)
endif()
But I think that you can determine system by:
include(CMakeDetermineSystem)
if (MSVC)
...

CMake CMAKE_AUTOMOC in cross compilation

I've following issue. I'm tring to use native mechanism build in CMake for cross compilation. I prepared following toolchain.cmake file:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_SYSROOT /tmp/filesystem)
set(tools /opt/gcc-linaro-arm-linux-gnueabihf-4.8-2014.04_linux/bin/arm-linux-gnueabihf)
set(CMAKE_C_COMPILER ${tools}-gcc)
set(CMAKE_CXX_COMPILER ${tools}-g++)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
and in target CMakeList.txt is use:
set(CMAKE_AUTOMOC ON)
I expect that when I use CMAKE_FIND_ROOT_PATH_MODE_PROGRAM set to NEVER the CMake, according to documentation, will use moc from HOST:
If set to NEVER, then the roots in CMAKE_FIND_ROOT_PATH will be
ignored and only the host system root will be used.
However it still try to use moc from TARGET arm image rootfs.
I try to refind the moc executable like in first answer from this post: How to include a certain Qt installation using CMake? but with no luck.
I also try to set the QT_MOC_EXECUTABLE variable to proper path from HOST rootfs instead of TARGET one but also with no luck there. I event think that this variable isn't use by CMake when CMAKE_AUTOMOC is set to ON since after forcing change this cached variable cmake still use moc from TARGET rootfs.
Any ideas how to resolve this issue?
# EDIT 1
I found that the automoc generates such file in build folder:
CMakeFiles/*target_name*_automoc.dir/AutogenInfo.cmake
And in my case such variable is set to wrong path:
set(AM_QT_MOC_EXECUTABLE "/tmp/filesystem/usr/lib/arm-linux-gnueabihf/qt5/bin/moc")
should be:
set(AM_QT_MOC_EXECUTABLE "/usr/bin/moc")
I set AM_QT_MOC_EXECUTABLE to correct value in main CMakeList.txt but still after mentioned file is generated with wrong path from TARGET rootfs.
I finally found the solution thanks to this post: How can I use CMake's AUTOMOC feature with a custom Qt package?. As I assumed the QT_MOC_EXECUTABLE isn't use directly by AUTOMOC.
Before first qt find_package following lines must be added:
set(QT_MOC_EXECUTABLE /usr/bin/moc)
add_executable(Qt5::moc IMPORTED)
set_property(TARGET Qt5::moc PROPERTY IMPORTED_LOCATION ${QT_MOC_EXECUTABLE})
The issue here was that not only the variable QT_MOC_EXECUTABLE has to be set to proper value but finally the automoc uses just Qt5:moc target which must be declared before any qt package will be included in CMakeList.txt file.
This same issue is with other qt tools so more generic option will be:
file(GLOB Qt_bin /usr/bin)
find_program(QT_MOC_EXECUTABLE qt_moc moc PATHS ${Qt_bin})
add_executable(Qt5::moc IMPORTED)
set_property(TARGET Qt5::moc PROPERTY IMPORTED_LOCATION ${QT_MOC_EXECUTABLE})

CMake: how to specify different CMakeFileList?

We created 3 different CMakeFileList files (CMakeFileList.engine, CMakeFileList.data and CMakeFileList.fep) for different build options within the same project. Does CMake support specifying CMakeFileList file as an argument? If not, what's the best way to accomplish our task by leveraging cmake? Any suggestion is appreciated.
In general I've done such kind of things by using the option() cmake command and providing just a single CMakeLists.txt file (what to build is decided inside according to the options, i.e. you can then also build everything in a single cmake/make run).
Example:
# the defaults (can be overridden on the command line)
option(BUILD_ENGINE "Build the Engine" ON)
option(BUILD_DATA "Build the Data" ON)
option(BUILD_FEP "Build the Fep" OFF)
if (BUILD_ENGINE)
# commands to build the Engine target or include(CMakeFileList.engine)
endif()
if (BUILD_DATA)
# commands to build the Data target or include(CMakeFileList.data)
endif()
if (BUILD_FEP)
# commands to build the Fep target or include(CMakeFileList.fep)
endif()
Then you can have everything in a single CMakeLists.txt and build what is needed each time, might it be multiple packages (if different than the defaults, you can switch on/off on the cmake command line). Or include the separate cmake lists as well (and just to make sure that they will work together if everything needs to be build).
Create main CMakeLists.txt file and conditionally use command include for use component-specific parts:
set(BUILD_TARGET "" CACHE STRING "Target to build")
if(BUILD_TARGET STREQUAL "engine")
include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeFileList.engine)
elseif(BUILD_TARGET STREQUAL "data")
include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeFileList.data)
elseif(BUILD_TARGET STREQUAL "fep")
include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeFileList.fep)
else()
message(FATAL_ERROR "Incorrect BUILD_TARGET")
endif()