CMake conditional for Release,Debug and Etc - cmake

I am working on a CMake project that need to set specific paths for each configuration type (e.g., RELEASE, DEBUG, MINSIZEREL and RELWITHDEBINFO) for a static library in linking process. Because I have different versions of my static libraries that is used on Debug and Release versions.
So far, I have done this below in order to set the folder "release" or "debug" for my static library that I am try to link:
# Here we need to determine our build type and set the each one specific 3rd library for our binary
if( CMAKE_BUILD_TYPE STREQUAL "Debug")
#set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/Install)
#message( STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}" )
set(THIRDLIBS_BUILD_TYPE "debug")
elseif( CMAKE_BUILD_TYPE STREQUAL "Release")
#SET(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/Install.deb)
#message( STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}" )
set(THIRDLIBS_BUILD_TYPE "release")
elseif( CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
#SET(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/Install.deb)
#message( STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}" )
set(THIRDLIBS_BUILD_TYPE "debug")
elseif( CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
#SET(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/Install.deb)
#message( STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}" )
set(THIRDLIBS_BUILD_TYPE "release")
else()
MESSAGE( STATUS "CMAKE_BUILD_TYPE not set yet ${CMAKE_BUILD_TYPE}" )
endif()
if (WIN32)
set(THIRD_LIBS
../external_libs/mylibalpha/win64/${THIRDLIBS_BUILD_TYPE}/libalpha
../external_libs/mylibbeta/win64/${THIRDLIBS_BUILD_TYPE}/libbeta
)
endif(WIN32)
However, in my Visual Studio, I noticed that on each project build type (RELEASE, DEBUG, MINSIZEREL and RELWITHDEBINFO) the ${THIRDLIBS_BUILD_TYPE} variable is empty and not getting properly set. I am basically getting an error on linking showing that cannot find the external_libs/mylibalpha/win64//libalpha.lib and I should be getting something like this external_libs/mylibalpha/win64/release/libalpha.lib
What is wrong with my CMakeLists.txt script?

Variable CMAKE_BUILD_TYPE has no sense with multiconfiguration generators, and Visual Studio is one of such generators.
Canonical way for define in CMake a pre-built library which have different locations for different configuration is to create IMPORTED library target with several properties IMPORTED_LOCATION_<CONFIG> to be set:
add_library(thirdPartyLib IMPORTED UNKNOWN)
set_target_properties(thirdPartyLib PROPERTIES
IMPORTED_LOCATION_RELEASE
${CMAKE_CURRENT_SOURCE_DIR}/external_libs/mylibalpha/win64/release/libalpha.a
IMPORTED_LOCATION_DEBUG
${CMAKE_CURRENT_SOURCE_DIR}/external_libs/mylibalpha/win64/debug/libalpha.a
)
Then you need to create a mapping between build types of the main project and build types of the IMPORTED target.
Such mapping could be created either on per-target basis, by setting properties MAP_IMPORTED_CONFIG_<CONFIG>:
set_target_properties(thirdPartyLib PROPERTIES
# For Debug version of the project use DEBUG-suffixed library location
MAP_IMPORTED_CONFIG_DEBUG DEBUG
# For Release version of the project use RELEASE-suffixed library location
MAP_IMPORTED_CONFIG_RELEASE RELEASE
# For ReleaseWithDebInfo version of the project use DEBUG-suffixed library location
MAP_IMPORTED_CONFIG_RELWITHDEBINFO DEBUG
# For MinSizeRel version of the project use RELEASE-suffixed library location
MAP_IMPORTED_CONFIG_MINSIZEREL RELEASE
)
Alternatively, the mapping could be created for all IMPORTED targets by setting variables CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>. Note, that these variables should be set before creation of IMPORTED target(s).
# For Debug version of the project use DEBUG-suffixed locations of IMPORTED targets
set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG DEBUG)
# For Release version of the project use RELEASE-suffixed locations of IMPORTED targets
set(CMAKE_MAP_IMPORTED_CONFIG_RELEASE RELEASE)
# For ReleaseWithDebInfo version of the project use DEBUG-suffixed locations of IMPORTED targets
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO DEBUG)
# For MinSizeRel version of the project use RELEASE-suffixed locations of IMPORTED targets
set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL RELEASE)
Having IMPORTED libraries and configurations mapping, using these libraries is quite straightforward:
target_link_libraries(my_exe PRIVATE thirdPartyLib)
Depending on the build type of the project, CMake will automatically select proper library for linking.

Related

Why my <package>-config.cmake have <package>_include_dir and <package>_librairies empty

I am trying to make a cross-platform CMake for my project (Windows and Linux).
I need to use external libraries (yaml-cpp). On Linux, I just had to do an apt get and use find_package. But on Windows, I need to append the CMAKE_MODULE_PATH in order for my program to find the yaml-cpp-config.cmake.
So I start by installing yaml-cpp (https://github.com/jbeder/yaml-cpp) with CMake GUI 3.16 and mingw32 (mingw32-make install).
I have tried the library on a hello world project, and it works fine.
cmake_minimum_required(VERSION 3.1)
project (yaml_test)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
message (STATUS "Yaml-cpp include = $ENV{YAML_CPP_INCLUDE_DIR}")
message (STATUS "Yaml-cpp library = $ENV{YAML_CPP_LIBRARIES}")
include_directories ($ENV{YAML_CPP_INCLUDE_DIR})
add_executable(yaml_test main.cpp)
target_link_libraries(yaml_test $ENV{YAML_CPP_LIBRARIES})
But now, I want to include the library in my project and use find_package. But the yaml-cpp-config.cmake looks like this:
# - Config file for the yaml-cpp package
# It defines the following variables
# YAML_CPP_INCLUDE_DIR - include directory
# YAML_CPP_LIBRARIES - libraries to link against
# Compute paths
get_filename_component(YAML_CPP_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
set(YAML_CPP_INCLUDE_DIR "")
# Our library dependencies (contains definitions for IMPORTED targets)
include("${YAML_CPP_CMAKE_DIR}/yaml-cpp-targets.cmake")
# These are IMPORTED targets created by yaml-cpp-targets.cmake
set(YAML_CPP_LIBRARIES "")
The YAML_CPP_INCLUDE_DIR and YAML_CPP_LIBRARIES variables are empty, and even if CMake found yaml-cpp-config.cmake, It doesn't work. So what do I have missing in the installation of yaml-cpp? Should I have set the paths by hand?
The absence of definition of YAML_CPP_INCLUDE_DIR and YAML_CPP_LIBRARIES variables is the issue with the yaml-cpp project which is already reported here.
Instead of variables described in this config file, use target yaml-cpp:
add_executable(yaml_test main.cpp)
# This provides both include directories and libraries.
target_link_libraries(yaml_test yaml-cpp)
Linking with an IMPORTED target (yaml-cpp in this case) is known as CMake "modern way".

Using protobuf with CMake's different build configurations (RelWithDebugInfo and Debug)

In my project I'm using protobuf 3.5. I need at least the debug and the RelWithDebugInfo configurations. To be able to build the project with protobuf debug libraries led to some problems alone:
I needed to build the protobuf libraries from source using both the release and the debug target since the _ITERATOR_DEBUG_LEVEL of my libraries (= 2) didn't match the level of the protobuf libraries (= 0). After building the debug libraries as well as the release libraries, compiling in debug configuration was possible.
Now, after changing back to RelWithDebugInfo I get the same error again, but now just the opposite: The _ITERATOR_DEBUG_LEVEL of my libraries is 0 and the level of the used protobuf libraries is 2.
When checking the linker configuration, I can see that my libraries are linked against the libprotobufd.lib. This makes sense since I've read somewhere that everything which is not Release will use the debug libraries, if available. And this leads to my problem:
I won't build my libraries in Release during development. It's most of the time RelWithDebugInfo. But the _ITERATOR_DEBUG_LEVEL for this configuration is obviously set to 0 (because it is a release configuration with additional information). But CMake then links against protobuf's debug libraries which are not compatible with the rest.
I'm now looking for a possibility to tell CMake to not use the debug version of the libraries but the release version instead without changing the CMake scripts of protobuf itself.
Normally my way to go would be to link different libraries depending on the actual build configuration. But unfortunately, the protobuf CMake configuration tries to handle this by itself.
# Load information for each installed configuration.
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB CONFIG_FILES "${_DIR}/protobuf-targets-*.cmake")
foreach(f ${CONFIG_FILES})
include(${f})
endforeach()
whilst the imported target is overwritten selected depending on the actual configuration:
protobuf-targets-release.cmake:
# Import target "protobuf::libprotobuf-lite" for configuration "Release"
set_property(TARGET protobuf::libprotobuf-lite APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(protobuf::libprotobuf-lite PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX"
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libprotobuf-lite.lib"
)
protobuf-targets-debug.cmake:
# Import target "protobuf::libprotobuf-lite" for configuration "Debug"
set_property(TARGET protobuf::libprotobuf-lite APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(protobuf::libprotobuf-lite PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX"
IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/libprotobuf-lited.lib"
)
whereas the linking in my CMakeLists.txt looks like this:
target_link_libraries(${PROJECT_NAME} PRIVATE
protobuf::libprotobuf
protobuf::libprotoc
)
I don't see any possibility here to specify the desired library. Normally I'd say I would specify it somehow like this:
target_link_libraries(MyEXE
debug protobuf::libprotobufd optimized protobuf::libprotobuf
debug protobuf::libprotocd optimized protobuf::libprotoc)
or wrap some fancy if-condition for the different build configurations around it. But due to protobuf effectively overwriting the target, I don't know how to extract the correct library for each build.
Any ideas?
I'm now looking for a possibility to tell CMake to not use the debug version of the libraries but the release version instead
Variables CMAKE_MAP_IMPORTED_CONFIG_ are intended for resolve exactly that problem:
# When build your project in RelWithDebugInfo configuration,
# try to use Release configuration of the *IMPORTED* libraries.
# If some IMPORTED library has no Release configuration, fallback to Debug one.
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBUGINFO Release Debug)
# Imported targets set with given call will be aware of the variable's set above
find_package(Protobuf REQUIRED)
# Simply link with an IMPORTED library.
target_link_libraries(MyExe protobuf::libprotobuf)
If you want to use Release configuration only from the specific IMPORTED libraries, but not all, you may set corresponded properties for these specific libraries:
# Firstly, create needed IMPORTED target.
find_package(Protobuf REQUIRED)
# Then adjust its properties.
# While the target is created by others, given property is specifically
# designed to be set in *your project*.
set_property(TARGET protobuf::libprotobuf PROPERTY MAP_IMPORTED_CONFIG_RELWITHDEBUGINFO Release Debug)
# Simply link with an IMPORTED library.
target_link_libraries(MyExe protobuf::libprotobuf)

What are the function calls like `BCI2000_INCLUDE( "SOURCEFILTER" )` in CMake?

I want to compile the Emotiv EPOC module for BCI2000. In that module CMakeLists file contains following(below the following error log).
The compiler gives the following error,
-- Selecting Windows SDK version to target Windows 10.0.10586.
CMake Error at CMakeLists.txt:21 (BCI2000_INCLUDE):
Unknown CMake command "BCI2000_INCLUDE".
CMake Warning (dev) in CMakeLists.txt:
No cmake_minimum_required command is present. A line of code such as
cmake_minimum_required(VERSION 3.10)```
CMakeLists.txt contains this,
###########################################################################
## $Id$
## Authors: griffin.milsap#gmail.com
## Description: Build information for the Emotiv module
# Set the executable name
SET( EXECUTABLE_NAME Emotiv )
# Set the project specific sources
SET( SRC_PROJECT
EmotivADC.cpp
lib/edk.imports.cpp
)
SET( HDR_PROJECT
EmotivADC.h
lib/edk.imports.h
)
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/lib/include )
BCI2000_INCLUDE( "SOURCEFILTER" )
IF( WIN32 )
# Create the signal source module
BCI2000_ADD_SIGNAL_SOURCE_MODULE(
"${EXECUTABLE_NAME}"
"${SRC_PROJECT}" "${HDR_PROJECT}"
"${CMAKE_CURRENT_SOURCE_DIR}/dll/edk.dll"
"${CMAKE_CURRENT_SOURCE_DIR}/dll/edk_utils.dll"
)
ELSE()
MESSAGE( "--- No Emotiv libraries for this OS. Will not build." )
ENDIF()
How can I fix this?
Looks like they are function calls, but how do I import those function calls from another file if they are declared somewhere?
The project has to be compiled according to the instructions in this,
https://www.bci2000.org/mediawiki/index.php/Programming_Reference:Build_System
And it says the supported Visual Studio editions are 9(2008) and 10 only or you can compile it with CodeBlocks with MinGW.
And the supportive modules should be included into the source folder of BCI2000 and compile it as a whole project not as seperate modules. Then it recognized the functions defined in the parent project.
There are cMakeList files for submodules. But they cannot be compiled on their own.

Project build configuration in CMake

My question is very similar to CMake : Changing name of Visual Studio and Xcode exectuables depending on configuration in a project generated by CMake. In that post the output file name will change according to the project configuration (Debug, Release and so on). I want to go further. When I know the configuration of the project, I want to tell the executable program to link different library names depending on project configurations. I was wondering whether there is a variable in CMake that can tell the project configuration. If there exists such a variable, my task will become easier:
if (Project_Configure_Name STREQUAL "Debug")
#do some thing
elseif (Project_Configure_Name STREQUAL "Release")
#do some thing
endif()
According to http://cmake.org/cmake/help/v2.8.8/cmake.html#command:target_link_libraries, you can specify libraries according to the configurations, for example:
target_link_libraries(mytarget
debug mydebuglibrary
optimized myreleaselibrary
)
Be careful that the optimized mode means every configuration that is not debug.
Following is a more complicated but more controllable solution:
Assuming you are linking to an imported library (not compiled in your cmake project), you can add it using:
add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION_RELEASE c:/path/to/foo.lib)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION_DEBUG c:/path/to/foo_d.lib)
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe foo)
See http://www.cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targets for more details.
There is always another way:
if(CMAKE_BUILD_TYPE MATCHES "release")
SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE})
else(CMAKE_BUILD_TYPE MATCHES "debug")
SET(CMAKE_BUILD_TYPE "debug")
endif(CMAKE_BUILD_TYPE MATCHES "release")
We can use the variable CMAKE_BUILD_TYPE. We can also change this variable at the beginning of invoking CMAKE:
cmake .. -DCMAKE_BUILD_TYPE:STRING=debug
Then we can use this variable as an indicator of build configuration.

How to add a custom build type to cmake ? (targeting make)

I'm trying to add a custom build type for my cmake build, targetted at code coverage.
I've found the FAQ entry about it : https://gitlab.kitware.com/cmake/community/wikis/FAQ#how-can-i-specify-my-own-configurations-for-generators-that-allow-it-
However, I can't make it work.
Here is my code, using cmake 2.8.5 :
message("* Adding build types...")
if(CMAKE_CONFIGURATION_TYPES)
list(APPEND CMAKE_CONFIGURATION_TYPES CodeCoverage)
list(REMOVE_DUPLICATES CMAKE_CONFIGURATION_TYPES)
set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING
"Add the configurations that we need"
FORCE)
message(" Available build types are now : ${CMAKE_CONFIGURATION_TYPES}")
else()
message(" XXX custom build types are not allowed...")
endif()
And I get "XXX custom build types are not allowed..."...
Found the problem : there is a confusion between adding custom builds and custom configurations :
configurations are for special tools like Visual Studio or XCode
build types are a much rawer feature
So to add a custom build type, there is no need to manipulate the CMAKE_CONFIGURATION_TYPES variable at all. Just set the corresponding variables and start using it, as explained :
SET(GCC_DEBUG_FLAGS "-g -Wall")
# Add new build types
message("* Adding build types...")
SET(CMAKE_CXX_FLAGS_COVERAGE
"${GCC_DEBUG_FLAGS} -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
SET(CMAKE_C_FLAGS_COVERAGE
"${GCC_DEBUG_FLAGS} -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
We may also want to udate the help string (along with setting the default build type by the way) :
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE Debug
CACHE STRING "Choose the type of build : None Debug Release RelWithDebInfo MinSizeRel Coverage."
FORCE)
ENDIF(NOT CMAKE_BUILD_TYPE)
message("* Current build type is : ${CMAKE_BUILD_TYPE}")
A bit of warning about trying to inherit default options from another build type doing this :
SET(CMAKE_CXX_FLAGS_COVERAGE
"${CMAKE_CXX_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
This is a bad idea because the extra options keep being appended every time the config is sourced again (every time you change something in it).