Browse Source

MSVC: Add abstraction for debug information format

Replace our hard-coded default for `/Zi` with a first-class abstraction
to select the debug information format an enumeration of logical
names.  We've long hesitated to do this because the idea of "debug
information format" touches on related concepts on several platforms.
Avoid that scope creep by simply defining an abstraction that applies
only when targeting the MSVC ABI on Windows.

Removing the old default flag requires a policy because existing
projects may rely on string processing to edit them and choose a
runtime library under the old behavior.  Add policy CMP0141 to
provide compatibility.

Fixes: #10189
Glen Chung 3 years ago
parent
commit
0e96a20478
32 changed files with 391 additions and 7 deletions
  1. 5 0
      Help/command/try_compile.rst
  2. 1 0
      Help/manual/cmake-policies.7.rst
  3. 1 0
      Help/manual/cmake-properties.7.rst
  4. 1 0
      Help/manual/cmake-variables.7.rst
  5. 55 0
      Help/policy/CMP0141.rst
  6. 15 0
      Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT-VALUES.txt
  7. 33 0
      Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst
  8. 7 0
      Help/release/dev/MsvcDebugInformationFormatAbstraction.rst
  9. 36 0
      Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst
  10. 22 2
      Modules/Platform/Windows-Clang.cmake
  11. 2 0
      Modules/Platform/Windows-Intel-Fortran.cmake
  12. 2 0
      Modules/Platform/Windows-IntelLLVM-Fortran.cmake
  13. 19 2
      Modules/Platform/Windows-MSVC.cmake
  14. 12 2
      Modules/Platform/Windows-NVIDIA-CUDA.cmake
  15. 11 0
      Source/cmCoreTryCompile.cxx
  16. 35 0
      Source/cmLocalGenerator.cxx
  17. 5 1
      Source/cmPolicies.h
  18. 1 0
      Source/cmTarget.cxx
  19. 1 0
      Tests/RunCMake/CMakeLists.txt
  20. 1 0
      Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-result.txt
  21. 5 0
      Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-stderr.txt
  22. 2 0
      Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW.cmake
  23. 4 0
      Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NoEffect.cmake
  24. 2 0
      Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-OLD.cmake
  25. 2 0
      Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-WARN.cmake
  26. 31 0
      Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-common.cmake
  27. 3 0
      Tests/RunCMake/MSVCDebugInformationFormat/CMakeLists.txt
  28. 6 0
      Tests/RunCMake/MSVCDebugInformationFormat/RunCMakeTest.cmake
  29. 0 0
      Tests/RunCMake/MSVCDebugInformationFormat/empty.cxx
  30. 46 0
      Tests/RunCMake/VS10Project/DebugInformationFormat-check.cmake
  31. 24 0
      Tests/RunCMake/VS10Project/DebugInformationFormat.cmake
  32. 1 0
      Tests/RunCMake/VS10Project/RunCMakeTest.cmake

+ 5 - 0
Help/command/try_compile.rst

@@ -244,3 +244,8 @@ a build configuration.
 .. versionadded:: 3.24
   The :variable:`CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES` variable may be
   set to disable passing platform variables into the test project.
+
+.. versionadded:: 3.25
+  If :policy:`CMP0141` is set to ``NEW``, one can use
+  :variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` to specify MSVC debug
+  information format.

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -58,6 +58,7 @@ Policies Introduced by CMake 3.25
 .. toctree::
    :maxdepth: 1
 
+   CMP0141: MSVC debug information format flags are selected by an abstraction. </policy/CMP0141>
    CMP0140: The return() command checks its arguments. </policy/CMP0140>
 
 Policies Introduced by CMake 3.24

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -340,6 +340,7 @@ Properties on Targets
    /prop_tgt/MACOSX_RPATH
    /prop_tgt/MANUALLY_ADDED_DEPENDENCIES
    /prop_tgt/MAP_IMPORTED_CONFIG_CONFIG
+   /prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT
    /prop_tgt/MSVC_RUNTIME_LIBRARY
    /prop_tgt/NAME
    /prop_tgt/NO_SONAME

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -486,6 +486,7 @@ Variables that Control the Build
    /variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG_INIT
    /variable/CMAKE_MODULE_LINKER_FLAGS_INIT
    /variable/CMAKE_MSVCIDE_RUN_PATH
+   /variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
    /variable/CMAKE_MSVC_RUNTIME_LIBRARY
    /variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX
    /variable/CMAKE_NO_BUILTIN_CHRPATH

+ 55 - 0
Help/policy/CMP0141.rst

@@ -0,0 +1,55 @@
+CMP0141
+-------
+
+.. versionadded:: 3.25
+
+MSVC debug information format flags are selected by an abstraction.
+
+Compilers targeting the MSVC ABI have flags to select the debug information
+format. Debug information format selection typically varies with build
+configuration.
+
+In CMake 3.24 and below, debug information format flags are added to
+the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache entries by CMake
+automatically.  This allows users to edit their cache entries to adjust the
+flags.  However, the presence of such default flags is problematic for
+projects that want to choose a different runtime library programmatically.
+In particular, it requires string editing of the
+:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variables with knowledge of the
+CMake builtin defaults so they can be replaced.
+
+CMake 3.25 and above prefer to leave the debug information format flags
+out of the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` values and instead
+offer a first-class abstraction.  The
+:variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` variable and
+:prop_tgt:`MSVC_DEBUG_INFORMATION_FORMAT` target property may be set to
+select the MSVC debug information format.  If they are not set, CMake
+enables debug information in debug configurations using the default value
+``$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>``, if supported by the
+compiler, and otherwise ``$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>``.
+
+This policy provides compatibility with projects that have not been updated
+to be aware of the abstraction.  The policy setting takes effect as of the
+first :command:`project` or :command:`enable_language` command that enables
+a language whose compiler targets the MSVC ABI.
+
+.. note::
+
+  Once the policy has taken effect at the top of a project, that choice
+  will be used throughout the tree.  In projects that have nested projects
+  in subdirectories, be sure to confirm if everything is working with the
+  selected policy behavior.
+
+The ``OLD`` behavior for this policy is to place MSVC debug information
+format flags in the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache
+entries and ignore the :variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT`
+abstraction.  The ``NEW`` behavior for this policy is to *not* place MSVC
+debug information format flags flags in the default cache entries and use
+the abstraction instead.
+
+This policy was introduced in CMake version 3.25.  Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt

+ 15 - 0
Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT-VALUES.txt

@@ -0,0 +1,15 @@
+``Embedded``
+  Compile with ``-Z7`` or equivalent flag(s) to produce object files
+  with full symbolic debugging information.
+``ProgramDatabase``
+  Compile with ``-Zi`` or equivalent flag(s) to produce a program
+  database that contains all the symbolic debugging information.
+``EditAndContinue``
+  Compile with ``-ZI`` or equivalent flag(s) to produce a program
+  database that supports the Edit and Continue feature.
+
+The value is ignored on non-MSVC compilers but an unsupported value will
+be rejected as an error when using a compiler targeting the MSVC ABI.
+
+The value may also be the empty string (``""``) in which case no debug
+information format flag will be added explicitly by CMake.

+ 33 - 0
Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst

@@ -0,0 +1,33 @@
+MSVC_DEBUG_INFORMATION_FORMAT
+-----------------------------
+
+.. versionadded:: 3.25
+
+Select debug information format targeting the MSVC ABI.
+
+The allowed values are:
+
+.. include:: MSVC_DEBUG_INFORMATION_FORMAT-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification.  For example, the code:
+
+.. code-block:: cmake
+
+  add_executable(foo foo.c)
+  set_property(TARGET foo PROPERTY
+    MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>")
+
+selects for the target ``foo`` the program database debug information format
+for the Debug configuration.
+
+If this property is not set, CMake selects a debug information format using
+the default value ``$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>``, if
+supported by the compiler, and otherwise
+``$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>``.
+
+.. note::
+
+  This property has effect only when policy :policy:`CMP0141` is set to ``NEW``
+  prior to the first :command:`project` or :command:`enable_language` command
+  that enables a language using a compiler targeting the MSVC ABI.

+ 7 - 0
Help/release/dev/MsvcDebugInformationFormatAbstraction.rst

@@ -0,0 +1,7 @@
+MsvcDebugInformationFormatAbstraction
+-------------------------------------
+
+* The :variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` variable and
+  :prop_tgt:`MSVC_DEBUG_INFORMATION_FORMAT` target property were introduced
+  to select the debug information format for compilers targeting the MSVC ABI.
+  See policy :policy:`CMP0141`.

+ 36 - 0
Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst

@@ -0,0 +1,36 @@
+CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
+-----------------------------------
+
+.. versionadded:: 3.25
+
+Select the MSVC debug information format targeting the MSVC ABI.
+This variable is used to initialize the
+:prop_tgt:`MSVC_DEBUG_INFORMATION_FORMAT` property on all targets as they are
+created.  It is also propagated by calls to the :command:`try_compile` command
+into the test project.
+
+The allowed values are:
+
+.. include:: ../prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification.  For example, the code:
+
+.. code-block:: cmake
+
+  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>")
+
+selects for all following targets the program database debug information format
+for the Debug configuration.
+
+If this variable is not set, the :prop_tgt:`MSVC_DEBUG_INFORMATION_FORMAT`
+target property will not be set automatically.  If that property is not set,
+CMake selects a debug information format using the default value
+``$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>``, if supported by the
+compiler, and otherwise ``$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>``.
+
+.. note::
+
+  This variable has effect only when policy :policy:`CMP0141` is set to ``NEW``
+  prior to the first :command:`project` or :command:`enable_language` command
+  that enables a language using a compiler targeting the MSVC ABI.

+ 22 - 2
Modules/Platform/Windows-Clang.cmake

@@ -96,10 +96,20 @@ macro(__windows_compiler_clang_gnu lang)
       set(_RTL_FLAGS " -D_DLL -D_MT -Xclang --dependent-lib=msvcrt")
     endif()
 
-    string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g -Xclang -gcodeview -O0${_RTL_FLAGS_DEBUG}")
+    if(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+      set(_DBG_FLAGS "")
+    else()
+      set(_DBG_FLAGS " -g -Xclang -gcodeview")
+    endif()
+
+    string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -O0${_DBG_FLAGS}${_RTL_FLAGS_DEBUG}")
     string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os -DNDEBUG${_RTL_FLAGS}")
     string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3 -DNDEBUG${_RTL_FLAGS}")
-    string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG -Xclang -gcodeview${_RTL_FLAGS}")
+    string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -DNDEBUG${_DBG_FLAGS}${_RTL_FLAGS}")
+
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -g -Xclang -gcodeview)
+    #set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase) # not supported by Clang
+    #set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue) # not supported by Clang
   endif()
   set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ")
   set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON)
@@ -109,6 +119,7 @@ macro(__windows_compiler_clang_gnu lang)
   set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Xclang -include-pch -Xclang <PCH_FILE> -Xclang -include -Xclang <PCH_HEADER>)
   set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Xclang -emit-pch -Xclang -include -Xclang <PCH_HEADER> -x ${__pch_header_${lang}})
 
+  unset(_DBG_FLAGS)
   unset(_RTL_FLAGS)
   unset(_RTL_FLAGS_DEBUG)
   string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_LOWER)
@@ -190,6 +201,7 @@ if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC"
     macro(__windows_compiler_clang_base lang)
       set(_COMPILE_${lang} "${_COMPILE_${lang}_MSVC}")
       __windows_compiler_msvc(${lang})
+      unset(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue) # -ZI not supported by Clang
       set(CMAKE_${lang}_COMPILE_OPTIONS_WARNING_AS_ERROR "-WX")
       set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-imsvc")
     endmacro()
@@ -202,6 +214,14 @@ if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC"
     endif()
     unset(__WINDOWS_CLANG_CMP0091)
 
+    cmake_policy(GET CMP0141 __WINDOWS_MSVC_CMP0141)
+    if(__WINDOWS_MSVC_CMP0141 STREQUAL "NEW")
+      set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>")
+    else()
+      set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT "")
+    endif()
+    unset(__WINDOWS_MSVC_CMP0141)
+
     set(CMAKE_BUILD_TYPE_INIT Debug)
 
     __enable_llvm_rc_preprocessing("" "-x c")

+ 2 - 0
Modules/Platform/Windows-Intel-Fortran.cmake

@@ -33,6 +33,8 @@ set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -th
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -threads -libs:dll)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -threads -libs:static -dbglibs)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -threads -libs:dll    -dbglibs)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -Z7)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase -Zi)
 
 # Intel Fortran for Windows supports single-threaded RTL but it is
 # not implemented by the Visual Studio integration.

+ 2 - 0
Modules/Platform/Windows-IntelLLVM-Fortran.cmake

@@ -33,6 +33,8 @@ set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -th
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -threads -libs:dll)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -threads -libs:static -dbglibs)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -threads -libs:dll    -dbglibs)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -Z7)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase -Zi)
 
 # Intel Fortran for Windows supports single-threaded RTL but it is
 # not implemented by the Visual Studio integration.

+ 19 - 2
Modules/Platform/Windows-MSVC.cmake

@@ -331,6 +331,13 @@ else()
 endif()
 unset(__WINDOWS_MSVC_CMP0091)
 
+cmake_policy(GET CMP0141 __WINDOWS_MSVC_CMP0141)
+if(__WINDOWS_MSVC_CMP0141 STREQUAL "NEW")
+  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT "$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>")
+else()
+  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT "")
+endif()
+unset(__WINDOWS_MSVC_CMP0141)
 
 # Features for LINK_LIBRARY generator expression
 if(MSVC_VERSION GREATER "1900")
@@ -441,6 +448,12 @@ macro(__windows_compiler_msvc lang)
     endif()
     unset(_cmp0092)
 
+    if(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+      set(_Zi "")
+    else()
+      set(_Zi " /Zi")
+    endif()
+
     if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "v[0-9]+_clang_.*")
       # note: MSVC 14 2015 Update 1 sets -fno-ms-compatibility by default, but this does not allow one to compile many projects
       # that include MS's own headers. CMake itself is affected project too.
@@ -451,20 +464,24 @@ macro(__windows_compiler_msvc lang)
       string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} -DNDEBUG") # TODO: Add '-Os' once VS generator maps it properly for Clang
     else()
       string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} /D_WINDOWS${_W3}${_FLAGS_${lang}}")
-      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} /Zi /Ob0 /Od ${_RTC1}")
+      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd}${_Zi} /Ob0 /Od ${_RTC1}")
       string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} /O2 /Ob2 /DNDEBUG")
-      string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} /Zi /O2 /Ob1 /DNDEBUG")
+      string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD}${_Zi} /O2 /Ob1 /DNDEBUG")
       string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} /O1 /Ob1 /DNDEBUG")
     endif()
     unset(_Wall)
     unset(_W3)
     unset(_MDd)
     unset(_MD)
+    unset(_Zi)
 
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -MT)
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -MD)
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -MTd)
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -MDd)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -Z7)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase -Zi)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue -ZI)
   endif()
   set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON)
 

+ 12 - 2
Modules/Platform/Windows-NVIDIA-CUDA.cmake

@@ -57,6 +57,12 @@ else()
   set(_MD "-MD ")
 endif()
 
+if(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+  set(_Zi "")
+else()
+  set(_Zi " -Zi")
+endif()
+
 cmake_policy(GET CMP0092 _cmp0092)
 if(_cmp0092 STREQUAL "NEW")
   set(_W3 "")
@@ -66,11 +72,12 @@ endif()
 unset(_cmp0092)
 
 string(APPEND CMAKE_CUDA_FLAGS_INIT " ${PLATFORM_DEFINES_CUDA} -D_WINDOWS -Xcompiler=\"${_W3}${_FLAGS_CXX}\"")
-string(APPEND CMAKE_CUDA_FLAGS_DEBUG_INIT " -Xcompiler=\"${_MDd}-Zi -Ob0 -Od ${_RTC1}\"")
+string(APPEND CMAKE_CUDA_FLAGS_DEBUG_INIT " -Xcompiler=\"${_MDd}${_Zi} -Ob0 -Od ${_RTC1}\"")
 string(APPEND CMAKE_CUDA_FLAGS_RELEASE_INIT " -Xcompiler=\"${_MD}-O2 -Ob2\" -DNDEBUG")
-string(APPEND CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT " -Xcompiler=\"${_MD}-Zi -O2 -Ob1\" -DNDEBUG")
+string(APPEND CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT " -Xcompiler=\"${_MD}${_Zi} -O2 -Ob1\" -DNDEBUG")
 string(APPEND CMAKE_CUDA_FLAGS_MINSIZEREL_INIT " -Xcompiler=\"${_MD}-O1 -Ob1\" -DNDEBUG")
 unset(_W3)
+unset(_Zi)
 unset(_MDd)
 unset(_MD)
 
@@ -78,6 +85,9 @@ set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -Xcomp
 set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -Xcompiler=-MD)
 set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -Xcompiler=-MTd)
 set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -Xcompiler=-MDd)
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -Xcompiler=-Z7)
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase -Xcompiler=-Zi)
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue -Xcompiler=-ZI)
 
 set(CMAKE_CUDA_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT}")
 

+ 11 - 0
Source/cmCoreTryCompile.cxx

@@ -94,6 +94,8 @@ std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES =
 std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED";
 std::string const kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT =
   "CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT";
+std::string const kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT =
+  "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT";
 
 /* GHS Multi platform variables */
 std::set<std::string> const ghs_platform_vars{
@@ -497,6 +499,14 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments,
               *cmp0123 == "NEW"_s ? "NEW" : "OLD");
     }
 
+    /* Set MSVC debug information format policy to match our selection.  */
+    if (cmValue msvcDebugInformationFormatDefault =
+          this->Makefile->GetDefinition(
+            kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)) {
+      fprintf(fout, "cmake_policy(SET CMP0141 %s)\n",
+              !msvcDebugInformationFormatDefault->empty() ? "NEW" : "OLD");
+    }
+
     /* Set cache/normal variable policy to match outer project.
        It may affect toolchain files.  */
     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) !=
@@ -849,6 +859,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments,
     vars.insert(kCMAKE_WARN_DEPRECATED);
     vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
     vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s);
+    vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s);
 
     if (cmValue varListStr = this->Makefile->GetDefinition(
           kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {

+ 35 - 0
Source/cmLocalGenerator.cxx

@@ -2041,6 +2041,41 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
       }
     }
   }
+
+  // Add MSVC debug information format flags. This is activated by the presence
+  // of a default selection whether or not it is overridden by a property.
+  cmValue msvcDebugInformationFormatDefault = this->Makefile->GetDefinition(
+    "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT");
+  if (cmNonempty(msvcDebugInformationFormatDefault)) {
+    cmValue msvcDebugInformationFormatValue =
+      target->GetProperty("MSVC_DEBUG_INFORMATION_FORMAT");
+    if (!msvcDebugInformationFormatValue) {
+      msvcDebugInformationFormatValue = msvcDebugInformationFormatDefault;
+    }
+    std::string const msvcDebugInformationFormat =
+      cmGeneratorExpression::Evaluate(*msvcDebugInformationFormatValue, this,
+                                      config, target);
+    if (!msvcDebugInformationFormat.empty()) {
+      if (cmValue msvcDebugInformationFormatOptions =
+            this->Makefile->GetDefinition(
+              cmStrCat("CMAKE_", lang,
+                       "_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_",
+                       msvcDebugInformationFormat))) {
+        this->AppendCompileOptions(flags, *msvcDebugInformationFormatOptions);
+      } else if ((this->Makefile->GetSafeDefinition(
+                    cmStrCat("CMAKE_", lang, "_COMPILER_ID")) == "MSVC"_s ||
+                  this->Makefile->GetSafeDefinition(
+                    cmStrCat("CMAKE_", lang, "_SIMULATE_ID")) == "MSVC"_s) &&
+                 !cmSystemTools::GetErrorOccurredFlag()) {
+        // The compiler uses the MSVC ABI so it needs a known runtime library.
+        this->IssueMessage(MessageType::FATAL_ERROR,
+                           cmStrCat("MSVC_DEBUG_INFORMATION_FORMAT value '",
+                                    msvcDebugInformationFormat,
+                                    "' not known for this ", lang,
+                                    " compiler."));
+      }
+    }
+  }
 }
 
 void cmLocalGenerator::AddLanguageFlagsForLinking(

+ 5 - 1
Source/cmPolicies.h

@@ -423,7 +423,11 @@ class cmMakefile;
     "The if() command supports path comparisons using PATH_EQUAL operator.",  \
     3, 24, 0, cmPolicies::WARN)                                               \
   SELECT(POLICY, CMP0140, "The return() command checks its arguments.", 3,    \
-         25, 0, cmPolicies::WARN)
+         25, 0, cmPolicies::WARN)                                             \
+  SELECT(                                                                     \
+    POLICY, CMP0141,                                                          \
+    "MSVC debug information format flags are selected by an abstraction.", 3, \
+    25, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 1 - 0
Source/cmTarget.cxx

@@ -563,6 +563,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     initProp("AUTORCC_OPTIONS");
     initProp("LINK_DEPENDS_NO_SHARED");
     initProp("LINK_INTERFACE_LIBRARIES");
+    initProp("MSVC_DEBUG_INFORMATION_FORMAT");
     initProp("MSVC_RUNTIME_LIBRARY");
     initProp("WATCOM_RUNTIME_LIBRARY");
     initProp("WIN32_EXECUTABLE");

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -359,6 +359,7 @@ if(MSVC)
   add_RunCMake_test(MSVCRuntimeLibrary)
   add_RunCMake_test(MSVCRuntimeTypeInfo)
   add_RunCMake_test(MSVCWarningFlags)
+  add_RunCMake_test(MSVCDebugInformationFormat)
 endif()
 if(XCODE_VERSION)
   set(ObjectLibrary_ARGS -DXCODE_VERSION=${XCODE_VERSION})

+ 1 - 0
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error in CMakeLists.txt:
+  MSVC_DEBUG_INFORMATION_FORMAT value 'BogusValue' not known for this (C|CXX)
+  compiler.
++
+CMake Generate step failed\.  Build files cannot be regenerated correctly\.$

+ 2 - 0
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0141 NEW)
+include(CMP0141-common.cmake)

+ 4 - 0
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NoEffect.cmake

@@ -0,0 +1,4 @@
+include(CMP0141-common.cmake)
+
+# Setting this policy after enable_language command has no effect.
+cmake_policy(SET CMP0141 NEW)

+ 2 - 0
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-OLD.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0141 OLD)
+include(CMP0141-common.cmake)

+ 2 - 0
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-WARN.cmake

@@ -0,0 +1,2 @@
+
+include(CMP0141-common.cmake)

+ 31 - 0
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-common.cmake

@@ -0,0 +1,31 @@
+enable_language(CXX)
+
+cmake_policy(GET CMP0141 cmp0141)
+if(cmp0141 STREQUAL "NEW")
+  if(NOT CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+    message(SEND_ERROR "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT not set under NEW behavior")
+  endif()
+else()
+  if(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+    message(SEND_ERROR "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT is set under OLD behavior")
+  endif()
+endif()
+
+if(cmp0141 STREQUAL "NEW")
+  if(CMAKE_CXX_FLAGS_DEBUG MATCHES "[/-]Zi( |$)")
+    message(SEND_ERROR "CMAKE_CXX_FLAGS_DEBUG has -Zi flags under NEW behavior.")
+  endif()
+  if(CMAKE_CXX_FLAGS_RELWITHDEBINFO MATCHES "[/-]Zi( |$)")
+    message(SEND_ERROR "CMAKE_CXX_FLAGS_RELWITHDEBINFO has -Zi flags under NEW behavior.")
+  endif()
+else()
+  if(NOT (CMAKE_CXX_FLAGS_DEBUG MATCHES "[/-]Zi( |$)"))
+    message(SEND_ERROR "CMAKE_CXX_FLAGS_DEBUG does not have -Zi flags under OLD behavior.")
+  endif()
+  if(NOT (CMAKE_CXX_FLAGS_RELWITHDEBINFO MATCHES "[/-]Zi( |$)"))
+    message(SEND_ERROR "CMAKE_CXX_FLAGS_RELWITHDEBINFO does not have -Zi flags under OLD behavior.")
+  endif()
+endif()
+
+set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT BogusValue)
+add_library(foo empty.cxx)

+ 3 - 0
Tests/RunCMake/MSVCDebugInformationFormat/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.24)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 6 - 0
Tests/RunCMake/MSVCDebugInformationFormat/RunCMakeTest.cmake

@@ -0,0 +1,6 @@
+include(RunCMake)
+
+run_cmake(CMP0141-WARN)
+run_cmake(CMP0141-OLD)
+run_cmake(CMP0141-NEW)
+run_cmake(CMP0141-NoEffect)

+ 0 - 0
Tests/RunCMake/MSVCDebugInformationFormat/empty.cxx


+ 46 - 0
Tests/RunCMake/VS10Project/DebugInformationFormat-check.cmake

@@ -0,0 +1,46 @@
+macro(DebugInformationFormat_check tgt Debug_expect Release_expect MinSizeRel_expect RelWithDebInfo_expect)
+  set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${tgt}.vcxproj")
+  if(NOT EXISTS "${vcProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not exist.")
+    return()
+  endif()
+
+  set(Debug_actual "")
+  set(Release_actual "")
+  set(MinSizeRel_actual "")
+  set(RelWithDebInfo_actual "")
+
+  file(STRINGS "${vcProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<ItemDefinitionGroup Condition=\"'\\$\\(Configuration\\)\\|\\$\\(Platform\\)'=='([^<>]+)\\|[A-Za-z0-9_]+'\">")
+      set(Configuration "${CMAKE_MATCH_1}")
+    endif()
+    if(line MATCHES "^ *<DebugInformationFormat>([^<>]+)</DebugInformationFormat>")
+      set(${Configuration}_actual "${CMAKE_MATCH_1}")
+    endif()
+  endforeach()
+
+  if (NOT "${Debug_actual}" STREQUAL "${Debug_expect}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj Debug Configuration has DebugInformationFormat '${Debug_actual}', not '${Debug_expect}'.")
+  endif()
+  if (NOT "${Release_actual}" STREQUAL "${Release_expect}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj Release Configuration has DebugInformationFormat '${Release_actual}', not '${Release_expect}'.")
+  endif()
+  if (NOT "${MinSizeRel_actual}" STREQUAL "${MinSizeRel_expect}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj MinSizeRel Configuration has DebugInformationFormat '${MinSizeRel_actual}', not '${MinSizeRel_expect}'.")
+  endif()
+  if (NOT "${RelWithDebInfo_actual}" STREQUAL "${RelWithDebInfo_expect}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj RelWithDebInfo Configuration has DebugInformationFormat '${RelWithDebInfo_actual}', not '${RelWithDebInfo_expect}'.")
+  endif()
+endmacro()
+
+DebugInformationFormat_check(default-C ProgramDatabase "" "" ProgramDatabase)
+DebugInformationFormat_check(default-CXX ProgramDatabase "" "" ProgramDatabase)
+DebugInformationFormat_check(empty-C "" "" "" "")
+DebugInformationFormat_check(empty-CXX "" "" "" "")
+DebugInformationFormat_check(Embedded-C OldStyle OldStyle OldStyle OldStyle)
+DebugInformationFormat_check(Embedded-CXX OldStyle OldStyle OldStyle OldStyle)
+DebugInformationFormat_check(ProgramDatabase-C ProgramDatabase ProgramDatabase ProgramDatabase ProgramDatabase)
+DebugInformationFormat_check(ProgramDatabase-CXX ProgramDatabase ProgramDatabase ProgramDatabase ProgramDatabase)
+DebugInformationFormat_check(EditAndContinue-C EditAndContinue EditAndContinue EditAndContinue EditAndContinue)
+DebugInformationFormat_check(EditAndContinue-CXX EditAndContinue EditAndContinue EditAndContinue EditAndContinue)

+ 24 - 0
Tests/RunCMake/VS10Project/DebugInformationFormat.cmake

@@ -0,0 +1,24 @@
+set(CMAKE_CONFIGURATION_TYPES Debug Release MinSizeRel RelWithDebInfo)
+cmake_policy(SET CMP0141 NEW)
+enable_language(C)
+enable_language(CXX)
+
+add_library(default-C empty.c)
+add_library(default-CXX empty.cxx)
+
+set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "")
+add_library(empty-C empty.c)
+add_library(empty-CXX empty.cxx)
+
+set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "Embedded")
+add_library(Embedded-C empty.c)
+add_library(Embedded-CXX empty.cxx)
+
+set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "ProgramDatabase")
+add_library(ProgramDatabase-C empty.c)
+add_library(ProgramDatabase-CXX empty.cxx)
+
+add_library(EditAndContinue-C empty.c)
+set_property(TARGET EditAndContinue-C PROPERTY MSVC_DEBUG_INFORMATION_FORMAT "EditAndContinue")
+add_library(EditAndContinue-CXX empty.cxx)
+set_property(TARGET EditAndContinue-CXX PROPERTY MSVC_DEBUG_INFORMATION_FORMAT "EditAndContinue")

+ 1 - 0
Tests/RunCMake/VS10Project/RunCMakeTest.cmake

@@ -88,3 +88,4 @@ run_cmake(VsDotnetStartupObject)
 run_cmake(VsDotnetTargetFramework)
 run_cmake(VsDotnetTargetFrameworkVersion)
 run_cmake(VsNoCompileBatching)
+run_cmake(DebugInformationFormat)