1

In CMake (ver. 3.8+), I'd like to copy all the settings for a build type to my custom build type.

I define a custom build type as:

# Add new configuration
set(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES} Deploy)
message("generated with config types:" ${CMAKE_CONFIGURATION_TYPES})

Ideally, I would not like to copy manually flags etc. because I may for example copy the CXX and linker flags, but forget C flags.

Is that possible?

starball
  • 20,030
  • 7
  • 43
  • 238
AndrewBloom
  • 2,171
  • 20
  • 30

1 Answers1

1

You'd need to copy all the the variables with <CONFIG> in their template name in the documentation.

You could hardcode it, or you could be fancy and write a function that does it based on the documented list of such variables. The following function takes the name of a source and destination ("from" and "to") build type names, and uses the output of cmake --help-variable-list to copy those source variables to the destination variables:

execute_process(
  COMMAND ${CMAKE_COMMAND} --help-variable-list
  OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake_var_full_list.txt"
)
file(STRINGS "${CMAKE_CURRENT_BINARY_DIR}/cmake_var_full_list.txt" VAR_FULL_LIST)
foreach(var ${VAR_FULL_LIST})
  if("${var}" MATCHES "<CONFIG>")
    if("${var}" MATCHES "<LANG>")
      foreach(lang C CXX CSharp CUDA OBJC OBJCXX Fortran HIP ISPC Swift ASM ASM_NASM ASM_MARMASM ASM_MASM ASM-ATT)
        # (supported languages list from https://cmake.org/cmake/help/latest/command/project.html)
        string(REPLACE "<LANG>" "${lang}" lang_var "${var}")
        list(APPEND CONFIG_VAR_LIST "${lang_var}")
      endforeach()
    else()
      list(APPEND CONFIG_VAR_LIST "${var}")
    endif()
  endif()
endforeach()
unset(VAR_FULL_LIST)

function(copy_configuration_type config_from config_to)
  string(TOUPPER "${config_from}" config_from)
  string(TOUPPER "${config_to}" config_to)
  foreach(config_var ${CONFIG_VAR_LIST})
    string(REPLACE "<CONFIG>" "${config_from}" config_var_from "${config_var}")
    string(REPLACE "<CONFIG>" "${config_to}"   config_var_to   "${config_var}")
    set("${config_var_to}" "${${config_var_from}}" PARENT_SCOPE)
  endforeach()
endfunction()

Example usage:

copy_configuration_type(DEBUG DEBUG2)
message("CMAKE_CXX_FLAGS_DEBUG2: ${CMAKE_CXX_FLAGS_DEBUG2}")

The wiki page sets these as cache variables (see the related CMake wiki entry). I didn't do that here, but you could adjust the call to set() to do so. If you do, you might also want to add logic for copying whether the cache variable is marked as advanced.

You may also want to copy whether the config is considered a debug configuration (see the DEBUG_CONFIGURATIONS global property (currently only used for a lesser-known feature of target_link_libraries, so you might not need to for your use-case))

Related questions: How to add a custom build type to CMake? (targeting make) and How to create a CMake configuration type that inherits from Release.

I raised an issue ticket to Kitware requesting that a standard function be added to do this here: https://gitlab.kitware.com/cmake/cmake/-/issues/24632.

starball
  • 20,030
  • 7
  • 43
  • 238
  • maybe this will affect the output folder used too? Like that the Debug2 executable will be written in the Debug output folder? – AndrewBloom Mar 27 '23 at 10:07
  • For my use case (VS code with VS toolchain) I ended up filtering the output directories using: `if (NOT "${config_var_to}" MATCHES "OUTPUT_DIRECTORY")` – AndrewBloom Mar 27 '23 at 11:26
  • @AndrewBloom that's a good point, but do note that by default, those output directory properties are left empty (you can add `set("${config_var_to}" "${${config_var_from}}")` then `message("${config_var_from}": "${${config_var_from}}")` to see for yourself) to signify that CMake will just generate as conventional for the buildsystem type. – starball Mar 27 '23 at 15:30
  • yes, indeed that's exactly what I did, and the *OUTPUT_DIRECTORY* variables where empty strings. I'm not sure if it may be a bug of vscode-cmake-tools or there's a way to distinguish a variable not set vs an empty string. Anyway, thanks for your very useful answer! – AndrewBloom Mar 27 '23 at 15:48
  • @AndrewBloom It's not really a matter of whether the variable is set or not, since once you read a target property into a variable... the variable will be set. What you're looking for is [`if("${foo}" STREQUAL "")`](https://cmake.org/cmake/help/latest/command/if.html#strequal). – starball Mar 27 '23 at 15:49