How can cmake_minimum_required required version impact generated files? - c++

I'm experiencing a strange behaviour where changing cmake_minimum_required affects files generated by CMake targetting Visual Studio 2019. According to the doc of cmake_minimum_required:
If the running version of CMake is lower than the required version it will stop processing the project and report an error
So it's just supposed to interrupt project generation.
But, if I create:
main.cpp:
int main()
{
#ifndef _DEBUG
#error "DEBUG flag not set"
#endif
return 0;
}
and CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.12)
project(hello_world)
set( CMAKE_CONFIGURATION_TYPES "Debug;Release;MyDebug" CACHE INTERNAL "" FORCE )
set( CMAKE_CXX_FLAGS_MYDEBUG "${CMAKE_CXX_FLAGS_DEBUG}" )
set( CMAKE_C_FLAGS_MYDEBUG "${CMAKE_C_FLAGS_DEBUG}" )
set( CMAKE_EXE_LINKER_FLAGS_MYDEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG}" )
set( CMAKE_SHARED_LINKER_FLAGS_MYDEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" )
set_property( GLOBAL PROPERTY DEBUG_CONFIGURATIONS "Debug;MyDebug" )
add_executable(app main.cpp)
If I generate this project with CMake 3.24.1 for Visual Studio 2019, then it builds correctly using MyDebug config as _DEBUG compilation flag is correctly set.
However, if I change cmake_minimum_required(VERSION 2.8.12) to cmake_minimum_required(VERSION 3.24.1), then it fails to build, reporting DEBUG flag not set, meaning _DEBUG compilation flag is not set anymore.
When I check vcproj file, I see that for MyDebug, <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> is changed to <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>.
Is this a CMake bug or am I doing something wrong?

So it's just supposed to interrupt project generation.
That's not the case at all!
cmake_minimum_required puts your project into a backwards compatibility mode consistent with the version specified. The "Policy Settings" section of that doc talks about this. There are a set of now over one hundred CMake policies that enable breaking improvements to the system. You can see the full list here: https://cmake.org/cmake/help/latest/manual/cmake-policies.7.html
The relevant policy here is CMP0091 which was introduced in CMake 3.15.
CMake 3.15 and above prefer to leave the MSVC runtime library selection flags out of the default CMAKE_<LANG>_FLAGS_<CONFIG> values and instead offer a first-class abstraction. The CMAKE_MSVC_RUNTIME_LIBRARY variable and MSVC_RUNTIME_LIBRARY target property may be set to select the MSVC runtime library. If they are not set then CMake uses the default value MultiThreaded$<$<CONFIG:Debug>:Debug>DLL which is equivalent to the original flags.
So to upgrade your project to a version of CMake newer than 3.15, you'll just need to override the default CMAKE_MSVC_RUNTIME_LIBRARY close to the top of your CMakeLists.txt file:
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug,MyDebug>:Debug>DLL"
CACHE STRING "Rule for selecting MSVC runtime")
This uses the $<CONFIG> generator expression to enable the debug runtime for your custom MyDebug config.

Of course cmake can change behaviour with its evolution.
As CMake evolves it is sometimes necessary to change existing behavior in order to fix bugs or improve implementations of existing features. *
If it just changes some behaviour in a new version, it introduces a policy, that can explicitly switch cmake to use an old behaviour. The OLD behavior of a policy becomes deprecated by definition and may be removed in a future version of CMake.
See what is changed in the list cmake-policies(7).
Also you can set cmake_minimum_required incrementing the major version number by one at a time, run cmake and see what policies were introduced in cmake dump. One of them is changing the behaviour of RuntimeLibrary and _DEBUG.

Related

How to check for current CMake build type in C++?

A little background: I'm writing a game in SFML and I want certain lines of code to be ommitted in Release build. I want to check during compile time for build type, so that is doesn't impact game's performance.
void Tile::draw(sf::RenderTarget& target, sf::RenderStates states) const {
states.transform *= getTransform();
target.draw(m_sprite, states);
#if(DEBUG)
target.draw(m_collision_shape, states);
#endif
}
I'm relatively new to CMake world, so I don't even know if it's possible.
EDIT:
It's probably worth mentioning I use ninja as a build system.
Add a macro from cmake in your code:
// CMakeLists.txt
add_executable(your_game yoru_soruces.c)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(your_game PRIVATE DEBUG=1)
else()
target_compile_definitions(your_game PRIVATE DEBUG=0)
endif()
You could also instead use the standard macro NDEBUG, #if !NDEBUG, however it's better to leave it for asserts only. NDEBUG is not defined for CMAKE_BUILD_TYPE=Debug builds.
That's how you might do it in general (working for any generator):
add_executable(${PROJECT_NAME} main.cpp)
target_compile_definitions(${PROJECT_NAME} PRIVATE "DEBUG=$<IF:$<CONFIG:Debug>,1,0>")
If you need it for every target you can use the following in the top CMake file (in case you have multiple with add_subdirectory) and before (it is not required but it is less confusing) you created a target. Also as Alex Reinking rightfully corrected we can use a simpler condition, because $<CONFIG:Debug> already gives 1 or 0.
So applying everything described above we get something like this:
add_compile_definitions("DEBUG=$<CONFIG:Debug>")
add_executable(${PROJECT_NAME} main.cpp)

CMake with MSVC and spdlog warnings

I'm using CMake to build a project that includes the spdlog library. At present, I'm doing so with Visual Studio, but I'm more used to Linux/GCC for what it's worth. Anyway, I'm having a heck of a time getting MSVC to treat spdlog as the external dependency that it is. Here's what I've got:
CMakeLists.txt from the root:
cmake_minimum_required(VERSION 3.16)
project(spdlogtest)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
find_package(spdlog REQUIRED)
add_executable(
spdlogtest
main.cpp
)
target_include_directories(
spdlogtest SYSTEM PUBLIC
${spdlog_SOURCE_DIR}/include/
)
target_compile_options(
spdlogtest PUBLIC
/Od /EHsc /nologo /MP /sdl /Wall
/external:W0
)
target_compile_definitions(
spdlogtest PUBLIC
DEBUG
)
set_target_properties(
spdlogtest PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
BINARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/debug/bin
BINARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/release/bin
)
Findspdlog.cmake:
message(STATUS "Pulling in external spdlog")
include(FetchContent)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG eb3220622e73a4889eee355ffa37972b3cac3df5 # v1.9.2
)
FetchContent_MakeAvailable(spdlog)
And finally main.cpp is just the quickstart example from the GitHub page.
#include "spdlog/spdlog.h"
int main()
{
spdlog::info("Welcome to spdlog!");
spdlog::error("Some error message with arg: {}", 1);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:<30}", "left aligned");
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed..");
// change log pattern
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
// Compile time log levels
// define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message");
}
Basically, if I've got the compiler set to /W4, everything compiles without a hitch. If I set it to /Wall though, I get 100+ warnings that mostly seem to come from the libfmt included in spdlog. I've tried using an external libfmt, but achieve much the same result.
I prefer /Wall (and -pedantic) while I'm writing my own code. Holding other people to that standard is ridiculous though, so mark the fetched project as a system include and you're good, right? I certainly thought so.
The CMakeLists.txt from the root produces the massive warnings, as do the variations I've attempted. On the plus side, I think I'm better at CMake than I was when I started.
This suggestion worked for me some time in the past, but doesn't seem to affect behavior here. Adding the flag /external:I${spdlog_SOURCE_DIR}/include directly to target_compile_options() also doesn't affect anything. I also tried brute forcing it and using GLOB_RECURSE to mark all of the files in ${spdlog_SOURCE_DIR} as explicitly /W0 after fetching them, but no luck there either.
If anyone can point me in the right direction and explain why what I'm trying to do is failing, I'd be very grateful.

Cannot set __cplusplus to C++17 standard with Visual Studio and CMake

I've a CMake project that's opened with Visual Studio 2019. I need c++17 features for my code, so I've set the corresponding flag in the CMakeLists.txt
cmake_minimum_required (VERSION 3.10.0)
project (datalog)
message (STATUS "Building project ${PROJECT_NAME}")
find_package(stxxl CONFIG REQUIRED)
include_directories (${CMAKE_SOURCE_DIR}/src)
set (PROJECT_SRC
main.cpp
)
add_executable (${PROJECT_NAME} ${PROJECT_SRC})
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
target_link_libraries(${PROJECT_NAME} stxxl)
When I build I obtain a lot of errors, because the library that I'm linking, stxxl, installed with vcpkg, has the following piece of code:
STXXL_BEGIN_NAMESPACE
template <class Type>
struct compat_unique_ptr {
#if __cplusplus >= 201103L && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40400)
typedef std::unique_ptr<Type> result;
#else
// auto_ptr is inherently broken and is deprecated by unique_ptr in c++0x
typedef std::auto_ptr<Type> result;
#endif
};
STXXL_END_NAMESPACE
The problem is that __cplusplus has value 199711L instead of the correct value 201703L, so code tries to use auto_ptr that's removed in C++17 standard.
I've also tried to set the flag manually in Visual Studio, adding this section in the CmakeLists.txt
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17 /Zc:__cplusplus")
endif()
But nothing changes. I'm also using some C++17 features, so going back at the moment is not an option.
I can manually update the stxxl header file to get rid of the macro, but it's a workaround, and every developer should create the same fix. Instead, I'd like to know why I'm getting the error and how to set the macro to the correct value with Visual Studio and CMake. Do you have any suggestion?
As far as i can tell from this, you need to add /Zc:__cplusplus manually (at least for now).
Your CMakeList snippet worked for me, are you sure, that __cplusplus is actually set incorrectly and it's not just the __GNUC__ Macro, that's missing?
That said, I'd recommend to test if the version of VisualStudio is actually new enough:
# /Zc:__cplusplus is required to make __cplusplus accurate
# /Zc:__cplusplus is available starting with Visual Studio 2017 version 15.7
# (according to https://learn.microsoft.com/en-us/cpp/build/reference/zc-cplusplus)
# That version is equivalent to _MSC_VER==1914
# (according to https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019)
# CMake's ${MSVC_VERSION} is equivalent to _MSC_VER
# (according to https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html#variable:MSVC_VERSION)
if ((MSVC) AND (MSVC_VERSION GREATER_EQUAL 1914))
target_compile_options(stxxl INTERFACE "/Zc:__cplusplus")
endif()
Note: I used target_compile_options to add the necessary option to the interface of your library target. This way all targets, that depend on that library (via target_link_libraries) will be compiled with that flag, but targets, that don't need it (and might be incompatible with it) won't get it.
If you want to add the flag to one of your own targets, you can use target_compile_options(stxxl PUBLIC "/Zc:__cplusplus") (PUBLIC instead of INTERFACE). INTERFACE means the option is used when other targets depend on that target, PRIVATE means the option is used for the target itself and PUBLIC means both. (IMHO this artical explains it well.)
Apart from that, you shouldn't set the c++ standard version via compile flags, but instead using target_compile_features if the newer version is required and using
set_target_properties(your_target_name PROPERTIES CXX_STANDARD 17) if you just wish to use the newer version if it is available.
In your case the latter is probably sufficient as your codesnippet has a fallback if the c++ version is older.
If changing the code of stxxl (via pull request, etc.) is an option, the code could test _MSVC_LANG and _MSC_VER. This way it would work without requiring /Zc:__cplusplus.
I think what you need is target_compile_definitions, where you can define arbitrary macros in CMake and pass to compiler.

How to enable _LIBCPP_DEBUG_LEVEL>=2 in xcode

When reading the std library implementation I could see lots of checks enabled by #if _LIBCPP_DEBUG_LEVEL >= 2 conditions. I tried to add _LIBCPP_DEBUG_LEVEL = 3 in xcode preprocessor options, but <iterator> doesn't compile anymore:
#if _LIBCPP_DEBUG_LEVEL >= 2
__get_db()->__insert_i(this); <----- the error is on this line
#endif
Is there something else I'm missing here to use a higher debug level for the standard library?
According to the libc++ documentation:
Debug mode is currently not functional. Defining _LIBCPP_DEBUG will result in fairly nasty compile errors.
So that is probably the source of that.
Baum mit Augen's answer that _LIBCPP_DEBUG_LEVEL >= 2 standard library code for lower level iterator debugging is basically unusable, appears to still be the case with the out-of-the-box Xcode 11.6. But if you're up for extra work or just want more specifics, read on...
Enabling _LIBCPP_DEBUG_LEVEL code is enabling LLVM's Debug Mode. According to that documentation, enabling this debug mode is done by defining the _LIBCPP_DEBUG to a value of 0 (that "enables most of libc++’s assertions") or 1 (that "enables 'iterator debugging' which provides additional assertions about the validity of iterators used by the program").
In XCode 11.6, I've found that adding _LIBCPP_DEBUG=0 to a project's Debug Preprocessor Macros setting, compiles and links and indeed adds additional checks - at least checking against out of range references to std::vector elements via its [] operator. Enabling the _LIBCPP_DEBUG_LEVEL >= 2 code is done by instead setting _LIBCPP_DEBUG=1. That alone however won't fully link as not all the necessary symbols are found. Specifically, the std::__1::__libcpp_db::__insert_c(void*, std::__1::__c_node* (*)(void*, void*, std::__1::__c_node*)) symbol is reported as undefined and maybe others as well. Presumably, XCode 11.6 did not ship with this built into the standard C++ library it provides. If anybody knows better or could corroborate this, I'd appreciate hearing from them.
With the following extra work, I've been able to enable libc++'s assertions plus the additional iterator debugging capabilities:
Install a recent LLVM package somewhere that doesn't interfere with Xcode's included LLVM package. I installed LLVM 10.0.1 using Homebrew and I was able to access it then under /usr/local/Cellar/llvm/10.0.1.
Have Xcode recognize the Xcode tool chain support that this new LLVM package provides. I added a symbolic link to /usr/local/Cellar/llvm/10.0.1/Toolchains/LLVM10.0.1.xctoolchain into Xcode's /Applications/Xcode.app/Contents/Developer/Toolchains directory. I'd done that while Xcode was running and it immediately saw the new tool chain but I had to restart Xcode to get it to compile with it.
Under Xcode's "Xcode" -> "Toolchains" submenu, select the new tool chain. My setup showed "Xcode 11.7" and "org.llvm.10.0.1" and I selected the latter to accomplish this.
Add the path to the new tool chain's usr/lib directory to the LIBRARY_SEARCH_PATHS declaration for resulting executable Targets. I did this by adding /usr/local/Cellar/llvm/10.0.1/Toolchains/LLVM10.0.1.xctoolchain/usr/lib to a project's executable target "Library Search Paths" "Debug" setting.
Have the path to the new tool chain's include directory override the normal standard library's include directory. I haven't confirmed that this is necessary but did this by adding to my library and executable targets' OTHER_CPLUSPLUSFLAGS declaration the value of -nostdinc++ -I/usr/local/Cellar/llvm/10.0.1/Toolchains/LLVM10.0.1.xctoolchain/usr/include/c++/v1. From within Xcode, I'd achieved that by setting the "Other C++ Flags" setting to include this value for the targets of interest. I suspect it's probably safer to make this change at the project level than target level.
Set the COMPILER_INDEX_STORE_ENABLE declaration to NO. This prevented "Unknown argument: '-index-store-path'" errors that stopped builds right from the beginning. I used Xcode's "Enable Index-While-Building Functionality" setting and set it to "No" for this.
Add _LIBCPP_DEBUG=1 to the GCC_PREPROCESSOR_DEFINITIONS declaration for the project. Did this by adding it to Xcode's "Preprocessor Macros" "Debug" setting.
For related Q&A about this, see Is it possible to enable _LIBCPP_DEBUG2 in the current Xcode 4.6.1 toolchain on Mountain Lion?.

#ifdef DEBUG with CMake independent from platform

I am using CMake for building my projects on Windows (Visual Studio) as well as on Linux machines(gcc). I'd like to mark some code as "debugging only", like with
#ifdef DEBUG
//some logging here
#endif
The question is: what compiler definition is available on all platforms in the CMake "Debug" build type? DEBUG seems not to exist. (I want to have the logging or whatever only when the build type is Debug.)
CMake adds -DNDEBUG to the CMAKE_C_FLAGS_{RELEASE, MINSIZEREL} by default. So, you can use #ifndef NDEBUG.
I would suggest that you add your own definition. The CMake symbol CMAKE_C_FLAGS_DEBUG can contain flags only used in debug mode. For example:
C:
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DMY_DEBUG")
C++:
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DMY_DEBUG")
In your code you can then write the following:
#ifdef MY_DEBUG
// ...
#endif
(Maybe, you would have to use "/DMY_DEBUG" for visual studio.)
In CMake >= 2.8, use target_compile_definitions:
target_compile_definitions(MyTarget PUBLIC "$<$<CONFIG:DEBUG>:DEBUG>")
When compiling in Debug mode, this will define the DEBUG symbol for use in your code. It will work even in IDEs like Visual Studio and Xcode for which cmake generates a single file for all compilation modes.
You have to do this for each target [1]. Alternatively you can use add_compile_options (Cmake >= 3.0):
add_compile_options("$<$<CONFIG:DEBUG>:-DDEBUG>")
Note that recent versions of Visual C++ (at least since VS2015) allow either / or - for parameters, so it should work fine across compilers. This command is also useful for other compile options you might like to add ("/O2" in release mode for MSVC or "-O3" for release mode in G++/Clang)
[1] : Note: in CMake >= 3.12 (currently beta) there is also an add_compile_definitions that supports generator expressions, which affects all targets.
I'm using the following in my CMakeLists.txt:
set(IS_DEBUG_BUILD CMAKE_BUILD_TYPE STREQUAL "Debug")
# Indication to the code that this is a debug build
if (${IS_DEBUG_BUILD})
add_compile_definitions(__DEBUG__)
endif ()
Then, in my code, I can write:
#ifdef __DEBUG__
// blablabla
#edif
My minimum CMake version:
cmake_minimum_required(VERSION 3.16.3)