Cmake: Specifiy config specific settings for multi-config cmake project - c++

Cmake novice here, I am currently trying to convert a cmake project that was developed with only single configuration in mind to a multi-config project which can generate visual studio files.
My problem that I can not solve is that in the cmake project there exist logic depending on the variable CMAKE_BUILD_TYPE such as:
set(ENABLE_DEBUG TRUE)
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
set(ENABLE_DEBUG FALSE)
)
Since for multi-config cmake the CMAKE_BUILD_TYPE is empty this way of doing it does not work. The variable ENABLE_DEBUG is then in the cmake project used for stuff such as:
Case 1: Adding libraries to only debug config
if(ENABLE_DEBUG)
list(APPEND LIB_SRC src/lib_debug.cpp)
endif()
add_library(LIB OBJECT LIB_SRC)
Case 2: Adding preprocessor flags to only debug config
if(ENABLE_DEBUG)
add_definitions(...)
endif()
So what I wonder is if anyone has a workaround for one or both of the cases above that would work for multi-config cmake projects, i.e so that I can specify library additions and preprocessor flags without depending on the CMAKE_BUILD_TYPE variable. Or even better if there a config specific way of setting the ENABLE_DEBUG without depending on the CMAKE_BUILD_TYPE variable?

In CMake common way for config-specific settings for multi-config build tools is using generator expressions.
Command add_library allows to use generator expressions for source files. E.g. this:
add_library(mylib common.c $<$<CONFIG:DEBUG>:debug.c>)
creates a library consisted from common.c in all configuration plus additional debug.c in Debug configuration.
Documentation for add_definitions doesn't note usage of generator expressions, but documentation for target_compile_definitions does:
target_compile_definitions(mylib PUBLIC $<$<CONFIG:DEBUG>:-DDEBUG_VAR>)

Related

AUTOMOC set to true makes fail cmake build

I'm at the very first day of Qt + Cmake and Conan, trying to make things work. I'm not using qmake because I'll integrate everything into a bigger project using cmake.
By following QT's tutorial, I figured out that I need to compile QT macros, and for that there's a useful AUTOMOC CMake property, as suggested here.
The point is that it's making me fail cmake builds.
My conanfile.txt:
[requires]
qt/5.15.2
[generators]
cmake
My CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(qttest)
set(CMAKE_CXX_STANDARD 20)
set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC TRUE)
set (PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
with the following output:
CMake Warning (dev) in CMakeLists.txt:
AUTOGEN: No valid Qt version found for target qttest. AUTOMOC disabled.
Consider adding:
find_package(Qt<QTVERSION> COMPONENTS Core)
to your CMakeLists.txt file.
This warning is for project developers. Use -Wno-dev to suppress it.
ouch, but adding the find doesn't make things better:
CMake Warning at CMakeLists.txt:6 (find_package):
By not providing "FindQt5.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "Qt5", but
CMake did not find one.
Could not find a package configuration file provided by "Qt5" with any of
the following names:
Qt5Config.cmake
qt5-config.cmake
Actually the project compiles, Qt is there in its conan dir:
matteo#MacBook-Pro-de-matteo 96a68a791abfc7a246f2bc28aa2f6fc210be0f9f % cd ~/.conan/data/qt
matteo#MacBook-Pro-de-matteo qt % ls
5.15.2 6.2.2
matteo#MacBook-Pro-de-matteo qt %
how could I enable it, or make things easier to compile it along with cmake?
You need to tell CMake, where to find Qt.
So, as CMake suggests by itself:
find_package(Qt5 COMPONENTS Core)
for the most basic stuff, you might want to add some of the other components later.
Depending on the system you are working on and your Qt installation, you need to tell CMake where to search for the package configuration files (second error message). CMake has some default directories, where it looks for these files, but obviously, there is none. On Linux, this can be solved by installing Qt with a package manager (this will install the CMake config files to one of the Qt default locations). If you are on Windows or if you installed Qt to a different location, this can be solved by providing the path with the PREFIX_PATH-variable.
cmake -B $BUILD_DIR -S $SOURCE_DIR -DCMAKE_PREFIX_PATH=$QT_INSTALL_PATH/5.15.2/$ARCHITECTURE $OTHER_OPTIONS
(You can have different versions installed in the same installation path, that's why Qt adds an other folder with the version number. And you can have different compilers/architectures. On Windows for example, you might have a mingw73_32 and a msvc2017 folder to choose.)
As already mentioned in the comments, a project is no CMake target. CMake targets are either libraries (add_library), executables (add_executable) or custom targets (add_custom_target); the project is not. If you want to set the AUTOMOC property target wise, that's ok and even suggested by CMake, but you can also set it globally by using:
set(CMAKE_AUTOMOC ON)

Generate preprocessor definitions based on CMAKE_CONFIGURATION_TYPES

I want to generate a Visual Studio project with two configurations using cmake. I want cmake to define different preprocessor symbols for these configurations.
I generate the project with the following command
cmake -B intermediate/cmake -G "Visual Studio 16 2019" -T v142 -DCMAKE_GENERATOR_PLATFORM=x64
In my CMakeLists.txt I define configurations:
if(CMAKE_CONFIGURATION_TYPES)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
endif()
Now, how do I define the preprocessor definitions per configuration? The quick search advises against using if(CMAKE_BUILD_TYPE STREQUAL "Release") because it doesn't work for multiconfiguration generators.
The conventional way to handle configuration-specific details in a multi-configuration generator is through the use of generator expressions.
Generator expressions allow you to specify symbols, values, flags, etc that will only be expanded at generation time based on the current state of the generator (such as the current build configuration).
For example, you can define custom pre-processor definitions with the -D flag with target_compile_definitions:
target_compile_definitions(MyTarget PRIVATE
$<$<CONFIG:Debug>:DEBUG_ONLY=1>
$<$<CONFIG:Release>:RELEASE_ONLY=2>
FOO=3
)
(This example is for PRIVATE definitions. Replace this with PUBLIC or INTERFACE as needed.)
This adds -DDEBUG_ONLY=1 to MyTarget for Debug builds, -DRELEASE_ONLY=2 for Release builds, and -DFOO=3 for all builds.
Also see this relevant/similar question: Cmake: Specifiy config specific settings for multi-config cmake project

How to find (configure) Qt for cmake project of iOS?

I install Qt via online installer on my macOS (Qt for iOS and macOS).
And qmake project works just fine, now I need compile with cmake Qt project:
cmake_minimum_required(VERSION 3.5)
project(demo LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 COMPONENTS Core Quick REQUIRED)
And I can not find Qt.
Direct build configuration:
cmake .. -GXcode -DQt5_DIR=/Users/user/Qt/5.14.1/clang_64/lib/cmake/Qt5
...
-- Configuring done
-- Generating done
-- Build files have been written to:
Cross-compiling for iOS:
cmake .. -GXcode -DCMAKE_SYSTEM_NAME=iOS -DQt5_DIR=/Users/user/Qt/5.14.1/ios/lib/cmake/Qt5
...
CMake Error at /Users/user/Qt/5.14.1/ios/lib/cmake/Qt5/Qt5Config.cmake:28 (find_package):
Could not find a package configuration file provided by "Qt5Core" with any
of the following names:
Qt5CoreConfig.cmake
qt5core-config.cmake
Add the installation prefix of "Qt5Core" to CMAKE_PREFIX_PATH or set
"Qt5Core_DIR" to a directory containing one of the above files. If
"Qt5Core" provides a separate development package or SDK, be sure it has
been installed.
Obviously Qt5CoreConfig.cmake is just fine and it is in relatively the same place as for "direct" compilation, but for some reason cmake can not find it
$ find /Users/user/Qt/5.14.1/ios/lib/cmake/ -name Qt5CoreConfig.cmake
/Users/user/Qt/5.14.1/ios/lib/cmake//Qt5Core/Qt5CoreConfig.cmake
How can I fix this?
When building for iOS CMAKE_SYSROOT is set to iPhoneOS.sdk path.
find_package:
The CMAKE_SYSROOT variable can also be used to specify exactly one directory to use as a prefix. Setting CMAKE_SYSROOT also has other effects. See the documentation for that variable for more.
These variables are especially useful when cross-compiling to point to the root directory of the target environment and CMake will search there too. By default at first the directories listed in CMAKE_FIND_ROOT_PATH are searched, then the CMAKE_SYSROOT directory is searched, and then the non-rooted directories will be searched.
So, adding the option -DCMAKE_FIND_ROOT_PATH=/Users/user/Qt/5.14.1/ios will help.

Configure cmake to choose compiler based on environment

My current project directory looks like
myproject
/-build
/-include
/-somefile.h
/-somefile2.h
/-myproject.cpp
/-CMakeLists.txt
and current CMakeLists.txt looks like :
cmake_minimum_required (VERSION 3.1)
project (myproject)
add_executable(myproject myproject.cpp)
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
include_directories("${PROJECT_INCLUDE_DIR}")
include_directories("${PROJECT_SOURCE_DIR}")
I want to build this project in both x64Windows and x64Linux environment while keeping single cmake file. Currently I have Visual Studio 13 CE in Win and gcc in Linux. Is it possible that Cmake could intelligently choose the correct compiler depending on OS? And what changes should I make to CMakeLists for that?
I'm using normal stuff like STL and vanilla C++ ( no os dependent libraries) if that matters. Thanks
You want to add conditional code for each compiler:
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# Set the C++ and linker flags to GCC specifics here.
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Set the C++ and linker flags to VC++ specifics here.
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# Set the C++ and linker flags to Clang specifics here.
elseif()
The right compiler is set automatically at least on linux. On windows I don't know. However you could always overwrite the selected compiler by the environment variables CC and CXX. This changes the cmake configuration. The changes will only affect new build configurations. So don't forget to delete your old one before selecting a new compiler.
Edit: The compiler must be of course in your PATH variable. Otherwise CMake will not find it correctly.

What is the default build configuration of cmake

In this answer, it says Debug is the default cmake build configuration.
But I have a different observation:
I have following in my CMakeLists.txt to choose debug and release versions of a lib according to the current build configuration.
target_link_libraries(MyApp debug Widgets_d)
target_link_libraries(MyApp optimized Widgets)
It seems that when I invoke cmake without sepcifying -DCMAKE_BUILD_TYPE flag, Widgets is used instead of Widgets_d (When I delete Widgets and try to build, make complains that lib is not there). So that means by default the build configuration is optimized, not debug.
So what actually is the default build configuration? If it is debug, what could be wrong with my CMakelists.txt?
target_link_libraries with optimized keyword corresponds to all configurations, which are not debug.
Try adding message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") to your CMakeLists.txt to see the actual build type (I suppose it should be empty).
If depends on whether you are using a single-configuration generator (Makefiles) or a multi-configuration generator (Visual Studio, XCode).
The link cited in the question is about a multi-configuration generator. When using a multi-configuration generator, the configuration variable CMAKE_BUILD_TYPE is ignored. To select the configuration to build, cmake allows the switch --config, and this defaults to Debug. So
cmake --build .
in a multi-configuration project builds a Debug version.
However, when using a single-configuration generator, the switch --config is ignored. Only the configuration variable CMAKE_BUILD_TYPE is used to determine the build type, and this defaults to Release.
More background info on single- and multiconfiguration-generators in this answer.