Should I cache CMAKE_BUILD_TYPE? - cmake

If you don't set the CMAKE_BUILD_TYPE, then, by default, it doesn't get cached. Should I cache it? e.g. should I have:
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" )
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Type of build (Debug, Release etc." FORCE)
endif()
or just:
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" )
endif()
Or does it not really matter?

Caching of CMAKE_BUILD_TYPE is usefull for cmake-gui: user of your project would able to change a build type in a nice manner.
There is a good template for setting default CMAKE_BUILD_TYPE from developers:
# Set a default build type if none was specified
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Debug' as none was specified.")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
"MinSizeRel" "RelWithDebInfo")
endif()

CMAKE_BUILD_TYPE in CMake's Cache
The user would normally define CMAKE_BUILD_TYPE via the command line with cmake -D .... So this does generate a cached entry.
And even when you don't give a CMAKE_BUILD_TYPE for single configuration makefile generators, CMake will automatically create an empty cache entry (so the user can choose in e.g. cmake-gui):
//Choose the type of build, options are: None(CMAKE_CXX_FLAGS or
// CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.
CMAKE_BUILD_TYPE:STRING=
If no default for CMAKE_BUILD_TYPE is given by the CMake's Platform/Compiler definitions via CMAKE_BUILD_TYPE_INIT like for Windows-MSVC:
# default to Debug builds
set(CMAKE_BUILD_TYPE_INIT Debug)
Settings a Default for CMAKE_BUILD_TYPE
So yes, if you want to force a default that is visible in the GUI then set the cached variable (see #Tsyvarev answer).
I don't normally force the cached defaults, I just set a temporary value if none is given. This allows e.g. my project to be used as a sub-project.
But that's more a matter of your personal taste.
In my project's those default CMAKE_BUILD_TYPE checks look a little more complex, since I allow more use cases:
# Make RelWithDebInfo the default (it does e.g. add '-O2 -g -DNDEBUG' for GNU)
# If not in multi-configuration environments, no explicit build type
# is set by the user and if we are the root CMakeLists.txt file.
if (NOT CMAKE_CONFIGURATION_TYPES AND
NOT CMAKE_NO_BUILD_TYPE AND
NOT CMAKE_BUILD_TYPE AND
CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif()
if (CMAKE_BUILD_TYPE)
string(TOUPPER "_${CMAKE_BUILD_TYPE}" MY_PROJECT_BUILD_TYPE_EXT)
endif()
# Choose a configuration for our compiler tests
if (NOT CMAKE_CONFIGURATION_TYPES AND
NOT CMAKE_NO_BUILD_TYPE)
set(CMAKE_TRY_COMPILE_CONFIGURATION "${CMAKE_BUILD_TYPE}")
else()
set(CMAKE_TRY_COMPILE_CONFIGURATION RelWithDebInfo)
endif()
But probably I should update this code to take the new GENERATOR_IS_MULTI_CONFIG global property into account (instead of checking for CMAKE_NO_BUILD_TYPE).
References
"CMake and the Default Build Type" blog post by Marcus D. Hanwell
CMake Issue #16768: Add reliable way to detect multi-config generators

Related

How to make CLion use different CMake config for debug and release?

I built 2 separate debug and release versions of OpenCV. How can I switch between 2 builds when I debug my project? I tried this:
IF(CMAKE_BUILD_TYPE MATCHES DEBUG)
message(WARNING "debug mode")
find_package(OpenCV REQUIRED
PATHS /path/to/debug/build NO_DEFAULT_PATH)
ELSE()
message(WARNING "release mode")
find_package(OpenCV REQUIRED)
ENDIF()
but it doesn't work. It does show release mode when I build normally, but doesn't show debug mode or release mode when the debugger kicks in. My thought is that CMAKE_BUILD_TYPE will be set to Debug when I debug. Is that true?
Thanks for your help.
MATCHES in cmake if is case sensitive. So when comparing CMAKE_BUILD_TYPE you have to decide on case. It's popular to convert to string into upper case and do comparisons then:
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UP)
if (${CMAKE_BUILD_TYPE_UP} STREQUAL "DEBUG")
...
Or the best is to compare against the standard values defiuned in cmake docs: Possible values are empty, Debug, Release, RelWithDebInfo and MinSizeRel. Note that both cmake -DCMAKE_BUILD_TYPE=dEbUg and cmake -DCMAKE_BUILD_TYPE=DeBuG both will configure for Debug build, but the CMAKE_BUILD_TYPE variable will differ. So the safest way is to convert it to upper case.

How do I detect that I am cross-compiling in CMakeLists.txt?

The CMake documentation suggests that CMAKE_CROSSCOMPILING is set when cross-compiling. In my CMakeLists.txt I have the lines:
IF(CMAKE_CROSSCOMPILING)
message(STATUS "Cross-compiling so skipping unit tests.")
option(GAME_PORTAL_UNIT_TEST "Enable unit testing of Game Portal code" OFF)
ELSE()
message(STATUS "Enabling unit testing of Game Portal code")
option(GAME_PORTAL_UNIT_TEST "Enable unit testing of Game Portal code" ON)
ENDIF()
The output from running:
cmake -DCMAKE_TOOLCHAIN_FILE=../crosscompile/raspberry_pi/CMakeCross.txt .
Includes the text "Enabling unit testing of Game Portal code", so clearly this variable is not being set, or not so it evaluates to true anyway.
I tried modifying CMakeCross.txt to include:
set(CMAKE_CROSSCOMPILING ON CACHE BOOL "Cross-compiling" FORCE)
and after cleaning the old CMakeCache.txt and rerunning my cmake command I can see that the new CMakeCache.txt now includes this variable, but I still get the same result as previously with regards to the unit tests being enabled.
How can I reliably detect that I am cross-compiling so I can properly disable the unit tests?
As requested, the full cross-compile file is:
# Set minimum cmake version required for cross-compiling to work.
cmake_minimum_required(VERSION 2.6)
# Build with rm CMakeCache.txt; cmake -DCMAKE_TOOLCHAIN_FILE=/home/crosscompile/dev/raspberry_pi/CMakeCross.txt ..
# Set target system name.
SET (CMAKE_SYSTEM_NAME Linux)
# Set compiler name.
SET (CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
SET (CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
# Set path(s) to search for libraries/binaries/headers.
SET (CMAKE_FIND_ROOT_PATH /home/crosscompile/dev/raspberry_pi/rootfs/)
# Ensure only cross-compiler directories are searched.
SET (ONLY_CMAKE_FIND_ROOT_PATH TRUE)
# search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# Set output/install directory to safe place.
SET (CMAKE_INSTALL_PREFIX /home/crosscompile/dev/raspberry_pi/install/)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rpath-link=/lib/arm-linux-gnueabihf")
set(THREADS_PTHREAD_ARG 0)
set(CMAKE_CROSSCOMPILING ON CACHE BOOL "Cross-compiling" FORCE)
The test for CMAKE_CROSSCOMPILING must come after the "project" instruction in CMakeLists.txt.
With in-source builds, one need to manually cleanup build files when change configuration parameters a lot.
E.g., if you did native build before, and then decide to cross-compile, you need to perform manual cleanup: CMake cannot automatically adjust build directory from one build type to another.
This is one of the reasons why in-source builds are not recommended and should be replaced with out-of-source builds.
This is working in my example:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(STM32F4Examples C)
set(CMAKE_CROSSCOMPILE OFF CACHE BOOL "is crosscompiled")
message(STATUS "CMAKE_CROSSCOMPILE ${CMAKE_CROSSCOMPILE}")
CMakeToolChain_STM32F4.txt
# cmake toolchain
# Use this file with cmake -DCMAKE_TOOLCHAIN_FILE=[PATH/TO/This/FILE] PATH/TO/SOURCES
set(CMAKE_CROSSCOMPILE ON CACHE BOOL "is crosscompiled" FORCE)
This cmake -DCMAKE_TOOLCHAIN_FILE=.. command will set CMAKE_TOOLCHAIN_FILE.
Check cross compiling with following:
if (CMAKE_TOOLCHAIN_FILE)
# This is in cross compiling condition.
set(PROJECT_OUT_NAME ${PROJECT_NAME}.elf)
else ()
set(PROJECT_OUT_NAME ${PROJECT_NAME})
endif()
add_executable(${PROJECT_OUT_NAME} "main.cpp")

How to check if generator is a multi-config generator in a CMakeLists.txt

The Cmake FAQ
and
other
places
recommend to check CMAKE_CONFIGURATION_TYPES to recognize a multi-configuration generator. I have found several questions where this did not work (for example this one). The issue seems to be that the variable is not set the first time cmake is called.
I tested with the following file
cmake_minimum_required(VERSION 2.6)
if(CMAKE_CONFIGURATION_TYPES)
message("Multi-configuration generator")
else()
message("Single-configuration generator")
endif()
project(foo)
and called it like this
mkdir build
cd build
cmake -G "Visual Studio 12 2013" ..
and got Single-configuration generator.
How should I distinguish whether the current generator supports multiple configurations?
EDITED: Added information on checking and changing CMAKE_CONFIGURATION_TYPES
Check and Changing CMAKE_CONFIGURATION_TYPES
Taking the suggestions from this question you could check and change CMAKE_CONFIGURATION_TYPES, but be aware that there was a bug 0015577: The 'project' command overwrites CMAKE_CONFIGURATION_TYPES in CMake 3.2.2 that did break this behaviour for the initial VS solution generation (fixed with CMake 3.3.0):
cmake_minimum_required(VERSION 3.3)
project(foo NONE)
if(CMAKE_CONFIGURATION_TYPES)
message("Multi-configuration generator")
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "My multi config types" FORCE)
else()
message("Single-configuration generator")
endif()
enable_language(C CXX)
Preset CMAKE_CONFIGURATION_TYPES
If you just need a certain set of configurations for multi-configuration environments you can do (thanks to #Tsyvarev for the suggestion):
cmake_minimum_required(VERSION 2.8)
# NOTE: Only used in multi-configuration environments
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "My multi config types" FORCE)
project(foo)
None multi-configuration environments will just ignore it. But be aware that other CMake modules like findBoost.cmake, findCUDA.cmake may rely on CMAKE_CONFIGURATION_TYPES being empty for single-configuration environments (thanks again #Tsyvarev for the hint).
So a better solution would be adding toolchain files for all your supported generators. They are generally useful, because there you can handle all the toolchain/generator specific parts.
Here is an extract of my VSToolchain.txt:
# Reduce the config types to only Debug and Release
SET(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
# Standard is a console app. If you need a windows app, use WIN32 define in add_executable
set(CMAKE_WIN32_EXECUTABLE 0 CACHE INTERNAL "")
CMAKE_WIN32_EXECUTABLE is just there to show what kind of settings I have put in my Visual Studio toolchain file.
Another CMake command line solution is suggested here: How to create cmake build configuration without debug symbols and without optimizations?
Only Checking CMAKE_CONFIGURATION_TYPES
If you only want do check what CMake does set in CMAKE_CONFIGURATION_TYPES:
I just tested your above code with Visual Studio 2013 and MinGW/GCC (both with empty build directories). You just need one small change and move the check after the project() command:
project(foo)
message("CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES}")
if(CMAKE_CONFIGURATION_TYPES)
message("Multi-configuration generator")
else()
message("Single-configuration generator")
endif()
And I get for VS2013:
CMAKE_CONFIGURATION_TYPES Debug;Release;MinSizeRel;RelWithDebInfo
Multi-configuration generator
And for GCC:
CMAKE_CONFIGURATION_TYPES
Single-configuration generator
For more details about what CMake does see:
CMAKE_CONFIGURATION_TYPES set by EnableLanguage() in cmGlobalVisualStudio7Generator.cxx
CMake: In which Order are Files parsed (Cache, Toolchain, …)?
I see you are on CMake v2.6, but for anyone who is on v3.9+, v3.9 introduced the global property called GENERATOR_IS_MULTI_CONFIG:
Read-only property that is true on multi-configuration generators.
You can load the value into a CMake variable like so:
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
This very approach is recommended in "Professional CMake" by Craig Scott, along with explanations of the shortcomings of other approaches- especially those involving CMAKE_CONFIGURATION_TYPES. The book is $30 but the section I'm referring to is in the sample chapters.

Where to set CMAKE_CONFIGURATION_TYPES in a project with subprojects

Lets say I have a project with two independent subprojects. If I understood cmake correctly, the idea would be to have one root CMakeLists.txt defining a project(...) and then using add_subdirectory(...) to include the subprojects. Each subproject would have its own CMakeLists.txt defining its own project. This way projects can be build either together (using the root cmake file) or individually (using the subprojects cmake file).
I now would like to change the CMAKE_CONFIGURATION_TYPES. Should I do this in the root CMakeLists.txt or in each subproject, or both?
Changing it in the root would mean that building a subproject individually would offer the wrong configuration types; the other options would duplicate the cmake code. I think I'm missing something here.
Factorize out the code that sets up configuration-dependent settings. Create a file, say, SetUpConfigurations.cmake with this content:
if(NOT SET_UP_CONFIGURATIONS_DONE)
set(SET_UP_CONFIGURATIONS_DONE TRUE)
# No reason to set CMAKE_CONFIGURATION_TYPES if it's not a multiconfig generator
# Also no reason mess with CMAKE_BUILD_TYPE if it's a multiconfig generator.
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(isMultiConfig)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Profile" CACHE STRING "" FORCE)
else()
if(NOT CMAKE_BUILD_TYPE)
message("Defaulting to release build.")
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build")
# set the valid options for cmake-gui drop-down list
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;Profile")
endif()
# now set up the Profile configuration
set(CMAKE_C_FLAGS_PROFILE "...")
set(CMAKE_CXX_FLAGS_PROFILE "...")
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "...")
endif()
Then include(..) this file at the beginning of the CMakeLists.txt's.
You have two choices about where to put SetUpConfigurations.cmake, it depends on how you organize your projects, repositories:
The quick'n'dirty way: Copy and commit this script into each project that needs it. Its location will be fixed, relative to the CMakeLists.txt of the project. So you can include it, for example, with include(${CMAKE_CURRENT_SOURCE_DIR}/<...>/SetUpConfigurations.cmake)
The disciplined way: Maintain a repository with your custom CMake scripts, like this one. Each time you generate a project with the cmake command, you pass the path to this repository in the CMAKE_MODULE_PATH variable:
cmake -DCMAKE_MODULE_PATH=<dir-of-cmake-script-repo> ...
In this case include the script with include(SetUpConfigurations) (no .cmake extension).
A note about what a multiconfig generator is:
Xcode and Visual Studio are multiconfig generators. They respect the value of CMAKE_CONFIGURATION_TYPES but CMAKE_BUILD_TYPE has no effect since no concrete configuration is defined when the CMakeLists.txt is processed. It will be selected on the IDE's user interface later.
On the other hand, the makefile-style generators are not interested in CMAKE_CONFIGURATION_TYPES. CMAKE_BUILD_TYPE defines the configuration. It is a concrete value when the CMakeLists.txt file is processed but still: never make any decisions based on the value of CMAKE_BUILD_TYPE:
if(CMAKE_BUILD_TYPE STREQUAL "Release") # WRONG!
....
endif()
You project won't work as intended in multiconfig generators.
When use add_subdirectory into subproject dir, you propagate almost all variables into that subproject, which contradicts to "subproject independency".
Instead, it is better to build and install subproject using nested cmake call inside execute_process(). If you want to make some subproject's definitions available for top-level project, you need to "export" this definitions when subproject is installed. This question/answer post describes, how to do that.

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.