Have a CMake project default to the Release build type - cmake

How should I make my CMake project default to configuration for a Release rather than a Debug build?

You could try the following:
if (NOT EXISTS ${CMAKE_BINARY_DIR}/CMakeCache.txt)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
endif()
endif()

I started out with simplistic:
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
but thanks to #RaulLaasner's suggestions, I now do:
if (NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
endif()
and that seems to be more robust.
Edit: Upon later reflection, I've decided to not change the default from within the CMake script itself. Perhaps it's better to leave the default the same as with all other CMake scripts in the world basically.

Related

How can I get a target's output-file's name during CMake's configuration phase (not the generation phase)?

I know we can use $<TARGET_FILE_NAME:Foo> to get the filename for add_custom_command and add_custom_target during build time, but I can't seem to find the answer on out how to get the filename during config time. For example,
add_library(Foo SHARED foo.cpp foo.h)
The best I get is get_target_property(FOO_NAME Foo NAME), but ${FOO_NAME} is Foo, what I want is something like libFoo.so or libFoo.dylib depends on the platform. How can we get the target file name during cmake config time?
For context on why I thought I initially thought I needed to be able to do this, see this other question: In CMake how can I copy a target file to a custom location when target based generator expression for OUTPUT is not supported?.
Through a combination of the following:
variable/CMAKE_BUILD_TYPE
prop_tgt/TYPE
prop_tgt/FRAMEWORK
variable/CMAKE_STATIC_LIBRARY_PREFIX and variable/CMAKE_STATIC_LIBRARY_SUFFIX
variable/CMAKE_SHARED_LIBRARY_PREFIX and variable/CMAKE_SHARED_LIBRARY_SUFFIX
variable/CMAKE_SHARED_MODULE_PREFIX and variable/CMAKE_SHARED_MODULE_SUFFIX
variable/CMAKE_EXECUTABLE_SUFFIX
prop_tgt/OUTPUT_NAME and prop_tgt/OUTPUT_NAME_<CONFIG>
prop_tgt/ARCHIVE_OUTPUT_NAME and prop_tgt/ARCHIVE_OUTPUT_NAME_<CONFIG>
prop_tgt/LIBRARY_OUTPUT_NAME and prop_tgt/LIBRARY_OUTPUT_NAME_<CONFIG>
prop_tgt/RUNTIME_OUTPUT_NAME and prop_tgt/RUNTIME_OUTPUT_NAME_<CONFIG>
I believe you could do something like this:
# cmake_minimum_required(VERSION 3.x)
project(hello)
function(get_target_filename target outvar)
get_target_property(prop_type "${target}" TYPE)
get_target_property(prop_is_framework "${target}" FRAMEWORK)
get_target_property(prop_outname "${target}" OUTPUT_NAME)
get_target_property(prop_archive_outname "${target}" ARCHIVE_OUTPUT_NAME)
get_target_property(prop_library_outname "${target}" LIBRARY_OUTPUT_NAME)
get_target_property(prop_runtime_outname "${target}" RUNTIME_OUTPUT_NAME)
# message("prop_archive_outname: ${prop_archive_outname}")
# message("prop_library_outname: ${prop_library_outname}")
# message("prop_runtime_outname: ${prop_runtime_outname}")
if(DEFINED CMAKE_BUILD_TYPE)
get_target_property(prop_cfg_outname "${target}" "${OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
get_target_property(prop_archive_cfg_outname "${target}" "${ARCHIVE_OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
get_target_property(prop_library_cfg_outname "${target}" "${LIBRARY_OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
get_target_property(prop_runtime_cfg_outname "${target}" "${RUNTIME_OUTPUT_NAME}_${CMAKE_BUILD_TYPE}")
# message("prop_archive_cfg_outname: ${prop_archive_cfg_outname}")
# message("prop_library_cfg_outname: ${prop_library_cfg_outname}")
# message("prop_runtime_cfg_outname: ${prop_runtime_cfg_outname}")
if(NOT ("${prop_cfg_outname}" STREQUAL "prop_cfg_outname-NOTFOUND"))
set(prop_outname "${prop_cfg_outname}")
endif()
if(NOT ("${prop_archive_cfg_outname}" STREQUAL "prop_archive_cfg_outname-NOTFOUND"))
set(prop_archive_outname "${prop_archive_cfg_outname}")
endif()
if(NOT ("${prop_library_cfg_outname}" STREQUAL "prop_library_cfg_outname-NOTFOUND"))
set(prop_library_outname "${prop_library_cfg_outname}")
endif()
if(NOT ("${prop_runtime_cfg_outname}" STREQUAL "prop_runtime_cfg_outname-NOTFOUND"))
set(prop_runtime_outname "${prop_runtime_cfg_outname}")
endif()
endif()
set(outname "${target}")
if(NOT ("${prop_outname}" STREQUAL "prop_outname-NOTFOUND"))
set(outname "${prop_outname}")
endif()
if("${prop_is_framework}")
set(filename "${outname}")
elseif(prop_type STREQUAL "STATIC_LIBRARY")
if(NOT ("${prop_archive_outname}" STREQUAL "prop_archive_outname-NOTFOUND"))
set(outname "${prop_archive_outname}")
endif()
set(filename "${CMAKE_STATIC_LIBRARY_PREFIX}${outname}${CMAKE_STATIC_LIBRARY_SUFFIX}")
elseif(prop_type STREQUAL "MODULE_LIBRARY")
if(NOT ("${prop_library_outname}" STREQUAL "prop_library_outname-NOTFOUND"))
set(outname "${prop_library_outname}")
endif()
set(filename "${CMAKE_SHARED_MODULE_LIBRARY_PREFIX}${outname}${CMAKE_SHARED_MODULE_LIBRARY_SUFFIX}")
elseif(prop_type STREQUAL "SHARED_LIBRARY")
if(WIN32)
if(NOT ("${prop_runtime_outname}" STREQUAL "prop_runtime_outname-NOTFOUND"))
set(outname "${prop_runtime_outname}")
endif()
else()
if(NOT ("${prop_library_outname}" STREQUAL "prop_library_outname-NOTFOUND"))
set(outname "${prop_library_outname}")
endif()
endif()
set(filename "${CMAKE_SHARED_LIBRARY_PREFIX}${outname}${CMAKE_SHARED_LIBRARY_SUFFIX}")
elseif(prop_type STREQUAL "EXECUTABLE")
if(NOT ("${prop_runtime_outname}" STREQUAL "prop_runtime_outname-NOTFOUND"))
set(outname "${prop_runtime_outname}")
endif()
set(filename "${CMAKE_EXECUTABLE_PREFIX}${outname}${CMAKE_EXECUTABLE_SUFFIX}")
else()
message(FATAL_ERROR "target \"${target}\" is not of type STATIC_LIBRARY, MODULE_LIBRARY, SHARED_LIBRARY, or EXECUTABLE.")
endif()
set("${outvar}" "${filename}" PARENT_SCOPE)
endfunction()
add_library(static_lib STATIC test.cpp)
add_library(shared_lib SHARED test.cpp)
add_executable(executable test.cpp)
get_target_filename(static_lib static_lib_filename)
get_target_filename(shared_lib shared_lib_filename)
get_target_filename(executable executable_filename)
message(STATUS "static_lib_filename: ${static_lib_filename}")
message(STATUS "shared_lib_filename: ${shared_lib_filename}")
message(STATUS "executable_filename: ${executable_filename}")
The above is a basic implementation. It doesn't handle some (perhaps important) nuances like:
The fact that most of those target properties can themselves have generator expressions in them (see their docs), which, if it happens to you, I think you're out of luck.
The fact that CMAKE_BUILD_TYPE is only relevant for single-config generators- not multi-config generators.
https://cmake.org/cmake/help/latest/variable/CMAKE_EXECUTABLE_SUFFIX_LANG.html
Other language-specific overrides like CMAKE_SHARED_LIBRARY_PREFIX_<LANG>
You'd need to check if those exist and handle them if they do... except in honesty I'm not quite sure how, given that it doesn't seem like targets have a LANGUAGE property. Source files do, but that's not what we need here. One might need to go to the CMake Discourse to ask about this.
Note: If you want the full path to the target output file... oh boy...
prop_tgt/ARCHIVE_OUTPUT_DIRECTORY and prop_tgt/ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>
prop_tgt/LIBRARY_OUTPUT_DIRECTORY and prop_tgt/LIBRARY_OUTPUT_DIRECTORY_<CONFIG>
prop_tgt/RUNTIME_OUTPUT_DIRECTORY and prop_tgt/RUNTIME_OUTPUT_DIRECTORY_<CONFIG>
More fun notes: If you want to evaluate generator expressions recursively at generation time (for generator expressions that themselves evaluate to generator expressions), you can use the $<GENEX_EVAL:...> generator expression, but of course- that doesn't apply to this question, which is about configure time.
A little bit clumsy but the file name can be constructed in the following way:
set(FOO_NAME "${CMAKE_SHARED_LIBRARY_PREFIX}Foo${CMAKE_SHARED_LIBRARY_SUFFIX}")
See also cmake doc.
Note that the two variables will be overridden by the respective CMAKE_SHARED_LIBRARY_??FIX_<LANG> variables. So if that's a possibility, you need make sure you catch the right variable.
Let me add the final remark that the rationale behind CMake is you don't need to know. CMake operates on targets, not files. So whatever you're trying to achieve might be possible without getting the filename.

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.

CMake RPATH-handling

There is this wiki page about RPATH-handling in CMake: https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/RPATH-handling#always-full-rpath
They say, that you should only add a directory to RPATH, if it's not a system directory. This makes sense, so I was wondering why they set CMAKE_INSTALL_RPATH in any case. Is there something I missed, or is this just a faulty code snippet?
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
...
LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
IF("${isSystemDir}" STREQUAL "-1")
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
ENDIF("${isSystemDir}" STREQUAL "-1")

Should I cache CMAKE_BUILD_TYPE?

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

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.