Tuesday, February 6, 2024

[SOLVED] How can I copy debug symbols flags from one build configuration to another?

Issue

I created a new custom CMake build configuration called "Profile". I wish it to be based on the default flags of the "RelWithDebInfo". In other parts of my cmake I enable specific preprocessor definitions for this configuration.

This is how I set it:

set(ULTRA_ALLOWED_BUILD_TYPES Debug RelWithDebInfo Profile)

# Copy flags from RelWithDebInfo
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELWITHDEBINFO}" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}" CACHE STRING "" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}" CACHE STRING "" FORCE)
set(CMAKE_MODULE_LINKER_FLAGS_PROFILE "${CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO}" CACHE STRING "" FORCE)
set(CMAKE_STATIC_LINKER_FLAGS_PROFILE "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO}" CACHE STRING "" FORCE)
set(CMAKE_MAP_IMPORTED_CONFIG_PROFILE "Release") # see https://github.com/microsoft/vcpkg/discussions/19327 for why we need to do this

get_property(isMultiConfig GLOBAL
    PROPERTY GENERATOR_IS_MULTI_CONFIG
)

# For multi-configuration generators, use generator expressions
if(isMultiConfig)
    set(CMAKE_CONFIGURATION_TYPES ${ULTRA_ALLOWED_BUILD_TYPES} CACHE STRING "" FORCE)

# add_compile_definitions($<CONFIG:Profile>:ULTRA_PROFILING_ENABLED)
else()
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${ULTRA_ALLOWED_BUILD_TYPES}")

    if(NOT CMAKE_BUILD_TYPE OR(NOT CMAKE_BUILD_TYPE IN_LIST ULTRA_ALLOWED_BUILD_TYPES))
        message(FATAL_ERROR
            "Missing or invalid build type: ${CMAKE_BUILD_TYPE}\n"
            "Set CMAKE_BUILD_TYPE to one of : ${ULTRA_ALLOWED_BUILD_TYPES}"
        )
    endif()
endif()

This configuration works. however, the created project does not have debug symbols enabled (/Zi flag under MSVC)

So how can I copy the flags that enable debug symbols from the "RelWithDebInfo" configuration?


Solution

With a minimum required version of 3.25 and later CMake no longer adds the debug info flags to CMAKE_<LANG>_FLAGS_<CONFIG> directly when compiling with MSVC. Therefore you can't copy them from the RelWithDebInfo configuration to your custom Profile configuration, because they're not part of the RelWithDebInfo flag variables in the first place.

That change was made to allow users to more easily change them (e.g. to use /Z7 instead of /Zi), which previously required knowledge about which flags were set by CMake under which condition to then use string(REPLACE) to correctly replace them.

To control the type of debug info CMake now uses a target property called MSVC_DEBUG_INFORMATION_FORMAT, which is initialized to the value of CMAKE_DEBUG_INFORMATION_FORMAT. This target property can be set to one of three values:

  • ProgramDatabase: This will set /Zi
  • Embedded: This will set /Z7
  • EditAndContinue: This will set /ZI

If the user hasn't set a value, CMake will use $<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase> by default, i.e. use /Zi for the Debug and RelWithDebInfo configurations, and no debug info flags otherwise, including your custom configuration.

To fix this, you can add a

set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo,Profile>:ProgramDatabase>")

call in your top-level CMakeLists.txt before any targets are defined.

However, I'd strongly recommend not to add a custom configuration. The issue that a generator expression makes assumptions about the set of available configurations does not only affect CMake, but most libraries make use of similar generator expressions or otherwise implicitly depend on the set of configurations being fixed. If you ever add third-party libraries to be built from source as part of your project, either through FetchContent, a package-manager like vcpkg, or even by vendoring them in your repository, you're bound to run into these troubles. Even your own project currently has code to reject unknown configurations in single generator mode and thus wouldn't work well if someone else tried to include it in their own project with their custom configuration!

The better solution is to use a dedicated CMake toolchain file that sets the required flags for your compiler, and to then add a preset that uses that toolchain file. This has the added benefit that the toolchain file is reusable and you can build any CMake project with the same settings.



Answered By - Corristo
Answer Checked By - Timothy Miller (WPSolving Admin)