diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index 35b5ff6939..64e18e814f 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim @@ -305,6 +305,7 @@ syn keyword cmakeProperty contained \ MEASUREMENT \ MODIFIED \ MSVC_DEBUG_INFORMATION_FORMAT + \ MSVC_RUNTIME_CHECKS \ MSVC_RUNTIME_LIBRARY \ NAME \ NO_SONAME @@ -1533,6 +1534,7 @@ syn keyword cmakeVariable contained \ CMAKE_MODULE_PATH \ CMAKE_MSVCIDE_RUN_PATH \ CMAKE_MSVC_DEBUG_INFORMATION_FORMAT + \ CMAKE_MSVC_RUNTIME_CHECKS \ CMAKE_MSVC_RUNTIME_LIBRARY \ CMAKE_NETRC \ CMAKE_NETRC_FILE diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst index b7d5439247..181e8509c6 100644 --- a/Help/command/try_compile.rst +++ b/Help/command/try_compile.rst @@ -349,6 +349,7 @@ as needed to honor the state of the calling project: * :policy:`CMP0155` * :policy:`CMP0157` * :policy:`CMP0181` +* :policy:`CMP0184` .. versionadded:: 4.0 The current setting of :policy:`CMP0181` policy is propagated through to the @@ -417,6 +418,11 @@ configuration: propagated into the test project's build configuration when using the :ref:`whole-project signature `. +.. versionadded:: 4.0 + If :policy:`CMP0184` is set to ``NEW``, one can use + :variable:`CMAKE_MSVC_RUNTIME_CHECKS` to specify the enabled MSVC runtime + checks. + See Also ^^^^^^^^ diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 11849b9d81..f4f2971b5e 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -98,6 +98,7 @@ Policies Introduced by CMake 4.0 .. toctree:: :maxdepth: 1 + CMP0184: MSVC runtime checks flags are selected by an abstraction. CMP0183: add_feature_info() supports full Condition Syntax. CMP0182: Create shared library archives by default on AIX. CMP0181: Link command-line fragment variables are parsed and re-quoted. diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 8925b31225..224f1d845c 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -359,6 +359,7 @@ Properties on Targets /prop_tgt/MANUALLY_ADDED_DEPENDENCIES /prop_tgt/MAP_IMPORTED_CONFIG_CONFIG /prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT + /prop_tgt/MSVC_RUNTIME_CHECKS /prop_tgt/MSVC_RUNTIME_LIBRARY /prop_tgt/NAME /prop_tgt/NO_SONAME diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 8057bf474a..b367ffcd57 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -519,6 +519,7 @@ Variables that Control the Build /variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG_INIT /variable/CMAKE_MODULE_LINKER_FLAGS_INIT /variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT + /variable/CMAKE_MSVC_RUNTIME_CHECKS /variable/CMAKE_MSVC_RUNTIME_LIBRARY /variable/CMAKE_MSVCIDE_RUN_PATH /variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX diff --git a/Help/policy/CMP0141.rst b/Help/policy/CMP0141.rst index 1aef8483f4..ac3160c7f5 100644 --- a/Help/policy/CMP0141.rst +++ b/Help/policy/CMP0141.rst @@ -13,8 +13,8 @@ In CMake 3.24 and below, debug information format flags are added to the default :variable:`CMAKE__FLAGS_` 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 +projects that want to choose a different debug information format +programmatically. In particular, it requires string editing of the :variable:`CMAKE__FLAGS_` variables with knowledge of the CMake builtin defaults so they can be replaced. diff --git a/Help/policy/CMP0184.rst b/Help/policy/CMP0184.rst new file mode 100644 index 0000000000..a27b69618a --- /dev/null +++ b/Help/policy/CMP0184.rst @@ -0,0 +1,54 @@ +CMP0184 +------- + +.. versionadded:: 4.0 + +MSVC runtime checks flags are selected by an abstraction. + +Compilers targeting the MSVC ABI have flags to select the runtime checks. +Runtime checks selection typically varies with build +configuration. + +In CMake 3.31 and below, runtime checks flags are added to +the default :variable:`CMAKE__FLAGS_` 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 different runtime checks programmatically. +In particular, it requires string editing of the +:variable:`CMAKE__FLAGS_` variables with knowledge of the +CMake builtin defaults so they can be replaced. + +CMake 4.0 and above prefer to leave the runtime checks flags +out of the default :variable:`CMAKE__FLAGS_` values and instead +offer a first-class abstraction. The +:variable:`CMAKE_MSVC_RUNTIME_CHECKS` variable and +:prop_tgt:`MSVC_RUNTIME_CHECKS` target property may be set to +select the MSVC runtime checks. If they are not set, CMake +enables runtime checks in ``Debug`` configuration using the default value +``$<$:StackFrameErrorCheck;UninitializedVariable>``, if +supported by the compiler, or empty value otherwise. + +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 runtimes checks +flags in the default :variable:`CMAKE__FLAGS_` cache +entries and ignore the :variable:`CMAKE_MSVC_RUNTIME_CHECKS` +abstraction. The ``NEW`` behavior for this policy is to *not* place MSVC +runtime checks flags in the default cache entries and use +the abstraction instead. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.0 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt diff --git a/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst b/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst index 7e08b483c4..94ee20ddb0 100644 --- a/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst +++ b/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst @@ -19,7 +19,7 @@ support per-configuration specification. For example, the code: MSVC_DEBUG_INFORMATION_FORMAT "$<$:ProgramDatabase>") selects for the target ``foo`` the program database debug information format -for the Debug configuration. +for the ``Debug`` and ``RelWithDebInfo`` configurations. This property is initialized from the value of the :variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` variable, if it is set. diff --git a/Help/prop_tgt/MSVC_RUNTIME_CHECKS-VALUES.txt b/Help/prop_tgt/MSVC_RUNTIME_CHECKS-VALUES.txt new file mode 100644 index 0000000000..44df3bfc98 --- /dev/null +++ b/Help/prop_tgt/MSVC_RUNTIME_CHECKS-VALUES.txt @@ -0,0 +1,16 @@ +``PossibleDataLoss`` + Compile with ``-RTCc`` or equivalent flag(s) to enable possible + data loss checks. +``StackFrameErrorCheck`` + Compile with ``-RTCs`` or equivalent flag(s) to enable stack frame + error checks. +``UninitializedVariable`` + Compile with ``-RTCu`` or equivalent flag(s) to enable uninitialized + variables checks. + +The value is ignored on compilers not targeting the MSVC ABI, 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 runtime +error check flags will be added explicitly by CMake. diff --git a/Help/prop_tgt/MSVC_RUNTIME_CHECKS.rst b/Help/prop_tgt/MSVC_RUNTIME_CHECKS.rst new file mode 100644 index 0000000000..4dbcc04f9f --- /dev/null +++ b/Help/prop_tgt/MSVC_RUNTIME_CHECKS.rst @@ -0,0 +1,34 @@ +MSVC_RUNTIME_CHECKS +------------------- + +.. versionadded:: 4.0 + +Select the list of enabled runtime checks when targeting the MSVC ABI. + +The allowed values are: + +.. include:: MSVC_RUNTIME_CHECKS-VALUES.txt + +Use :manual:`generator expressions ` to +support per-configuration specification. For example, the code: + +.. code-block:: cmake + + add_executable(foo foo.c) + set_property(TARGET foo PROPERTY + MSVC_RUNTIME_CHECKS "$<$:PossibleDataLoss;UninitializedVariable>") + +enables for the target ``foo`` the possible data loss and uninitialized variables checks +for the ``Debug`` and ``RelWithDebInfo`` configurations. + +This property is initialized from the value of the +:variable:`CMAKE_MSVC_RUNTIME_CHECKS` variable, if it is set. +If this property is not set, CMake selects a runtime checks using +the default value ``$<$:StackFrameErrorCheck;UninitializedVariable>``, if +supported by the compiler, or empty value otherwise. + +.. note:: + + This property has effect only when policy :policy:`CMP0184` 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. diff --git a/Help/release/dev/msvc-runtime-checks.rst b/Help/release/dev/msvc-runtime-checks.rst new file mode 100644 index 0000000000..195610588b --- /dev/null +++ b/Help/release/dev/msvc-runtime-checks.rst @@ -0,0 +1,7 @@ +msvc-runtime-checks +------------------- + +* The :variable:`CMAKE_MSVC_RUNTIME_CHECKS` variable and + :prop_tgt:`MSVC_RUNTIME_CHECKS` target property were introduced + to select runtime checks for compilers targeting the MSVC ABI. + See policy :policy:`CMP0184`. diff --git a/Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst b/Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst index 80df8fc1fa..84c25847f8 100644 --- a/Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst +++ b/Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst @@ -21,7 +21,7 @@ support per-configuration specification. For example, the code: set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$:ProgramDatabase>") selects for all following targets the program database debug information format -for the Debug configuration. +for the ``Debug`` and ``RelWithDebInfo`` configurations. 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, diff --git a/Help/variable/CMAKE_MSVC_RUNTIME_CHECKS.rst b/Help/variable/CMAKE_MSVC_RUNTIME_CHECKS.rst new file mode 100644 index 0000000000..5ba85472ef --- /dev/null +++ b/Help/variable/CMAKE_MSVC_RUNTIME_CHECKS.rst @@ -0,0 +1,36 @@ +CMAKE_MSVC_RUNTIME_CHECKS +------------------------- + +.. versionadded:: 4.0 + +Select the list of enabled runtime checks when targeting the MSVC ABI. +This variable is used to initialize the +:prop_tgt:`MSVC_RUNTIME_CHECKS` 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_RUNTIME_CHECKS-VALUES.txt + +Use :manual:`generator expressions ` to +support per-configuration specification. For example, the code: + +.. code-block:: cmake + + set(CMAKE_MSVC_RUNTIME_CHECKS "$<$:PossibleDataLoss;UninitializedVariable>") + +enables for the target ``foo`` the possible data loss and uninitialized variables checks +for the ``Debug`` and ``RelWithDebInfo`` configurations. + +If this variable is not set, the :prop_tgt:`MSVC_RUNTIME_CHECKS` +target property will not be set automatically. If that property is not set, +CMake selects runtime checks using the default value +``$<$:StackFrameErrorCheck;UninitializedVariable>``, +if supported by the compiler, or empty value otherwise. + +.. note:: + + This variable has effect only when policy :policy:`CMP0184` 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. diff --git a/Modules/CMakeASM_MARMASMInformation.cmake b/Modules/CMakeASM_MARMASMInformation.cmake index 51bd313fe5..8101f9ecd8 100644 --- a/Modules/CMakeASM_MARMASMInformation.cmake +++ b/Modules/CMakeASM_MARMASMInformation.cmake @@ -11,12 +11,15 @@ set(CMAKE_ASM${ASM_DIALECT}_SOURCE_FILE_EXTENSIONS asm) set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT " -o ") set(CMAKE_ASM${ASM_DIALECT}_CREATE_STATIC_LIBRARY " /out: ") -# The ASM_MARMASM compiler id for this compiler is "MSVC", so fill out the runtime library table. +# The ASM_MARMASM compiler id for this compiler is "MSVC", so fill out the abstraction tables. +set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss "") +set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck "") +set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "") +set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "") - set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded "-g") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue "") diff --git a/Modules/CMakeASM_MASMInformation.cmake b/Modules/CMakeASM_MASMInformation.cmake index 64ae0701a2..6ee159c6aa 100644 --- a/Modules/CMakeASM_MASMInformation.cmake +++ b/Modules/CMakeASM_MASMInformation.cmake @@ -11,12 +11,15 @@ set(CMAKE_ASM${ASM_DIALECT}_SOURCE_FILE_EXTENSIONS asm) set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT " -c -Fo ") set(CMAKE_ASM${ASM_DIALECT}_CREATE_STATIC_LIBRARY " /out: ") -# The ASM_MASM compiler id for this compiler is "MSVC", so fill out the runtime library table. +# The ASM_MASM compiler id for this compiler is "MSVC", so fill out the abstraction tables. +set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss "") +set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck "") +set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "") +set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "") - set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded "-Zi") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase "") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue "") diff --git a/Modules/Platform/Windows-Clang-ASM.cmake b/Modules/Platform/Windows-Clang-ASM.cmake index c22e3b0b34..87abadebba 100644 --- a/Modules/Platform/Windows-Clang-ASM.cmake +++ b/Modules/Platform/Windows-Clang-ASM.cmake @@ -1,6 +1,10 @@ include(Platform/Windows-Clang) __windows_compiler_clang(ASM) +set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss "") +set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck "") +set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "") +set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu "") set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "") set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL "") set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug "") diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake index 8c6a491a29..3f57b50189 100644 --- a/Modules/Platform/Windows-Clang.cmake +++ b/Modules/Platform/Windows-Clang.cmake @@ -117,6 +117,13 @@ macro(__windows_compiler_clang_gnu lang) string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3 -DNDEBUG${_RTL_FLAGS}") string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -DNDEBUG${_DBG_FLAGS}${_RTL_FLAGS}") + # clang-cl accepts -RTC* flags but ignores them. Simulate this + # with the GNU-like drivers by simply passing no flags at all. + set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss "") + set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck "") + set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "") + set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu "") + 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 @@ -239,6 +246,14 @@ endmacro() endif() unset(__WINDOWS_MSVC_CMP0141) + cmake_policy(GET CMP0184 __WINDOWS_MSVC_CMP0184) + if(__WINDOWS_MSVC_CMP0184 STREQUAL "NEW") + set(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT "$<$:StackFrameErrorCheck;UninitializedVariable>") + else() + set(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT "") + endif() + unset(__WINDOWS_MSVC_CMP0184) + set(CMAKE_BUILD_TYPE_INIT Debug) __enable_llvm_rc_preprocessing("" "-x c") diff --git a/Modules/Platform/Windows-Intel-Fortran.cmake b/Modules/Platform/Windows-Intel-Fortran.cmake index c9b70d59b9..ad2e525bdf 100644 --- a/Modules/Platform/Windows-Intel-Fortran.cmake +++ b/Modules/Platform/Windows-Intel-Fortran.cmake @@ -29,6 +29,12 @@ unset(_LIBSDLL) unset(_DBGLIBS) unset(_THREADS) +# icl accepts -RTC* flags but ignores them. ifort accepts -RTCu only. +set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss "") +set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck "") +set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable -RTCu) +set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu "") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded -threads -libs:static) 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) diff --git a/Modules/Platform/Windows-IntelLLVM-Fortran.cmake b/Modules/Platform/Windows-IntelLLVM-Fortran.cmake index 202ba23d0f..c8566e0a1d 100644 --- a/Modules/Platform/Windows-IntelLLVM-Fortran.cmake +++ b/Modules/Platform/Windows-IntelLLVM-Fortran.cmake @@ -29,6 +29,12 @@ unset(_LIBSDLL) unset(_DBGLIBS) unset(_THREADS) +# icx accepts -RTC* flags but ignores them. ifx accepts -RTCu only. +set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss "") +set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck "") +set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable -RTCu) +set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu "") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded -threads -libs:static) 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) diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index c0f736327a..02404434a9 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake @@ -192,6 +192,9 @@ if(WINCE) set(_PLATFORM_DEFINES_CXX " /D${_MSVC_CXX_ARCHITECTURE_FAMILY} /D_${_MSVC_CXX_ARCHITECTURE_FAMILY_UPPER}_") set(_RTC1 "") + set(_RTCc "") + set(_RTCs "") + set(_RTCu "") set(_FLAGS_C "") set(_FLAGS_CXX "${_GR} /EHsc") @@ -248,17 +251,24 @@ else() if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "v[0-9]+_clang_.*") # Clang/C2 in MSVC14 Update 1 seems to not support -fsantinize (yet?) # set(_RTC1 "-fsantinize=memory,safe-stack") + # set(_RTCs "-fsantinize=safe-stack") + # set(_RTCu "-fsantinize=memory") set(_FLAGS_CXX " -frtti -fexceptions") else() - set(_RTC1 "/RTC1") + set(_RTC1 "-RTC1") + set(_RTCc "-RTCc") + set(_RTCs "-RTCs") + set(_RTCu "-RTCu") set(_FLAGS_CXX "${_GR} /EHsc") endif() set(CMAKE_C_STANDARD_LIBRARIES_INIT "kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib") else() - set(_RTC1 "/GZ") + set(_RTC1 "-GZ") + set(_RTCs "-GZ") set(_FLAGS_CXX "${_GR} /GX") set(CMAKE_C_STANDARD_LIBRARIES_INIT "kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib") endif() + set(_RTCsu "${_RTC1}") if((_MSVC_C_ARCHITECTURE_FAMILY STREQUAL "ARM64EC") OR (_MSVC_CXX_ARCHITECTURE_FAMILY STREQUAL "ARM64EC")) string(APPEND CMAKE_C_STANDARD_LIBRARIES_INIT " softintrin.lib") @@ -353,6 +363,13 @@ else() endif() unset(__WINDOWS_MSVC_CMP0141) +cmake_policy(GET CMP0184 __WINDOWS_MSVC_CMP0184) +if(__WINDOWS_MSVC_CMP0184 STREQUAL "NEW") + set(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT "$<$:StackFrameErrorCheck;UninitializedVariable>") +else() + set(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT "") +endif() +unset(__WINDOWS_MSVC_CMP0184) macro(__windows_compiler_msvc lang) if(NOT MSVC_VERSION LESS 1400) @@ -467,17 +484,23 @@ macro(__windows_compiler_msvc lang) set(_Zi " /Zi") endif() + if(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT) + set(_RTC1_local "") + else() + string(REPLACE " -" " /" _RTC1_local " ${_RTC1}") + 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. string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} -fms-extensions -fms-compatibility -D_WINDOWS${_Wall}${_FLAGS_${lang}}") - string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} -gline-tables-only -fno-inline -O0 ${_RTC1}") + string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} -gline-tables-only -fno-inline -O0${_RTC1_local}") string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} -O2 -DNDEBUG") string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} -gline-tables-only -O2 -fno-inline -DNDEBUG") 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_local}") 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_MINSIZEREL_INIT "${_MD} /O1 /Ob1 /DNDEBUG") @@ -487,7 +510,12 @@ macro(__windows_compiler_msvc lang) unset(_MDd) unset(_MD) unset(_Zi) + unset(_RTC1_local) + set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss "${_RTCc}") + set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck "${_RTCs}") + set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "${_RTCu}") + set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu "${_RTCsu}") 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) diff --git a/Modules/Platform/Windows-NVIDIA-CUDA.cmake b/Modules/Platform/Windows-NVIDIA-CUDA.cmake index 9333cb4f49..224aa8e05b 100644 --- a/Modules/Platform/Windows-NVIDIA-CUDA.cmake +++ b/Modules/Platform/Windows-NVIDIA-CUDA.cmake @@ -68,6 +68,12 @@ else() set(_Zi " -Zi") endif() +if(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT) + set(_RTC1_local "") +else() + string(REPLACE " -" " /" _RTC1_local " ${_RTC1}") +endif() + cmake_policy(GET CMP0092 _cmp0092) if(_cmp0092 STREQUAL "NEW") set(_W3 "") @@ -77,7 +83,7 @@ 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_local}\"") 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_MINSIZEREL_INIT " -Xcompiler=\"${_MD}-O1 -Ob1\" -DNDEBUG") @@ -85,7 +91,12 @@ unset(_W3) unset(_Zi) unset(_MDd) unset(_MD) +unset(_RTC1_local) +set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss "-Xcompiler=${_RTCc} -D_ALLOW_RTCc_IN_STL") +set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck "-Xcompiler=${_RTCs}") +set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "-Xcompiler=${_RTCu}") +set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu "-Xcompiler=${_RTCsu}") set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded -Xcompiler=-MT) set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL -Xcompiler=-MD) set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug -Xcompiler=-MTd) diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index c79c6b17ec..7d785ea471 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -110,6 +110,8 @@ 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"; +std::string const kCMAKE_MSVC_RUNTIME_CHECKS_DEFAULT = + "CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT"; /* GHS Multi platform variables */ std::set const ghs_platform_vars{ @@ -683,6 +685,13 @@ cm::optional cmCoreTryCompile::TryCompileCode( !msvcDebugInformationFormatDefault->empty() ? "NEW" : "OLD"); } + /* Set MSVC runtime checks policy to match our selection. */ + if (cmValue msvcRuntimeChecksDefault = + this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_CHECKS_DEFAULT)) { + fprintf(fout, "cmake_policy(SET CMP0184 %s)\n", + !msvcRuntimeChecksDefault->empty() ? "NEW" : "OLD"); + } + /* Set cache/normal variable policy to match outer project. It may affect toolchain files. */ if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) != @@ -1111,6 +1120,7 @@ cm::optional cmCoreTryCompile::TryCompileCode( vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s); vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s); vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s); + vars.emplace("CMAKE_MSVC_RUNTIME_CHECKS"_s); vars.emplace("CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS"_s); vars.emplace("CMAKE_VS_USE_DEBUG_LIBRARIES"_s); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 57f0cdb101..f58078a18b 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -2193,10 +2193,7 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" + msvcRuntimeLibrary)) { this->AppendCompileOptions(flags, *msvcRuntimeLibraryOptions); - } else if ((this->Makefile->GetSafeDefinition( - "CMAKE_" + lang + "_COMPILER_ID") == "MSVC" || - this->Makefile->GetSafeDefinition( - "CMAKE_" + lang + "_SIMULATE_ID") == "MSVC") && + } else if ((compiler == "MSVC" || compilerSimulateId == "MSVC") && !cmSystemTools::GetErrorOccurredFlag()) { // The compiler uses the MSVC ABI so it needs a known runtime library. this->IssueMessage(MessageType::FATAL_ERROR, @@ -2224,10 +2221,8 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, "CMAKE_" + lang + "_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_" + watcomRuntimeLibrary)) { this->AppendCompileOptions(flags, *watcomRuntimeLibraryOptions); - } else if ((this->Makefile->GetSafeDefinition( - "CMAKE_" + lang + "_COMPILER_ID") == "OpenWatcom" || - this->Makefile->GetSafeDefinition( - "CMAKE_" + lang + "_SIMULATE_ID") == "OpenWatcom") && + } else if ((compiler == "OpenWatcom" || + compilerSimulateId == "OpenWatcom") && !cmSystemTools::GetErrorOccurredFlag()) { // The compiler uses the Watcom ABI so it needs a known runtime // library. @@ -2239,6 +2234,49 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, } } + // Add MSVC runtime checks flags. This is activated by the presence + // of a default selection whether or not it is overridden by a property. + cmValue msvcRuntimeChecksDefault = + this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT"); + if (cmNonempty(msvcRuntimeChecksDefault)) { + cmValue msvcRuntimeChecksValue = + target->GetProperty("MSVC_RUNTIME_CHECKS"); + if (!msvcRuntimeChecksValue) { + msvcRuntimeChecksValue = msvcRuntimeChecksDefault; + } + cmList msvcRuntimeChecksList = cmGeneratorExpression::Evaluate( + *msvcRuntimeChecksValue, this, config, target); + msvcRuntimeChecksList.remove_duplicates(); + + // RTC1/RTCsu VS GUI workaround + std::string const stackFrameErrorCheck = "StackFrameErrorCheck"; + std::string const unitinitializedVariable = "UninitializedVariable"; + std::string const rtcSU = "RTCsu"; + if ((cm::contains(msvcRuntimeChecksList, stackFrameErrorCheck) && + cm::contains(msvcRuntimeChecksList, unitinitializedVariable)) || + cm::contains(msvcRuntimeChecksList, rtcSU)) { + msvcRuntimeChecksList.remove_items( + { stackFrameErrorCheck, unitinitializedVariable, rtcSU }); + msvcRuntimeChecksList.append(rtcSU); + } + + for (std::string const& msvcRuntimeChecks : msvcRuntimeChecksList) { + if (cmValue msvcRuntimeChecksOptions = + this->Makefile->GetDefinition(cmStrCat( + "CMAKE_", lang, + "_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_" + msvcRuntimeChecks))) { + this->AppendCompileOptions(flags, *msvcRuntimeChecksOptions); + } else if ((compiler == "MSVC" || compilerSimulateId == "MSVC") && + !cmSystemTools::GetErrorOccurredFlag()) { + // The compiler uses the MSVC ABI so it needs a known runtime checks. + this->IssueMessage(MessageType::FATAL_ERROR, + cmStrCat("MSVC_RUNTIME_CHECKS value '", + msvcRuntimeChecks, "' not known for this ", + lang, " compiler.")); + } + } + } + // Add MSVC debug information format flags if CMP0141 is NEW. if (cm::optional msvcDebugInformationFormat = this->GetMSVCDebugFormatName(config, target)) { @@ -2249,10 +2287,7 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, "_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) && + } else if ((compiler == "MSVC" || compilerSimulateId == "MSVC") && !cmSystemTools::GetErrorOccurredFlag()) { // The compiler uses the MSVC ABI so it needs a known runtime library. this->IssueMessage(MessageType::FATAL_ERROR, diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 057e3d82ff..60fa0b0518 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -549,7 +549,10 @@ class cmMakefile; SELECT(POLICY, CMP0182, \ "Create shared library archives by default on AIX.", 4, 0, 0, WARN) \ SELECT(POLICY, CMP0183, \ - "add_feature_info() supports full Condition Syntax.", 4, 0, 0, WARN) + "add_feature_info() supports full Condition Syntax.", 4, 0, 0, WARN) \ + SELECT(POLICY, CMP0184, \ + "MSVC runtime check flags are selected by an abstraction.", 4, 0, 0, \ + WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 8eaf1b3626..185f9d870a 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -330,6 +330,7 @@ TargetProperty const StaticTargetProperties[] = { { "OSX_ARCHITECTURES"_s, IC::CanCompileSources }, // ---- Windows { "MSVC_DEBUG_INFORMATION_FORMAT"_s, IC::CanCompileSources }, + { "MSVC_RUNTIME_CHECKS"_s, IC::CanCompileSources }, { "MSVC_RUNTIME_LIBRARY"_s, IC::CanCompileSources }, { "VS_JUST_MY_CODE_DEBUGGING"_s, IC::CanCompileSources }, { "VS_DEBUGGER_COMMAND"_s, IC::ExecutableTarget }, @@ -1763,6 +1764,7 @@ void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt) "OSX_ARCHITECTURES", // ---- Windows "MSVC_DEBUG_INFORMATION_FORMAT", + "MSVC_RUNTIME_CHECKS", "MSVC_RUNTIME_LIBRARY", "VS_PLATFORM_TOOLSET", // ---- OpenWatcom diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index f422458556..6617455643 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -2128,6 +2128,14 @@ if(BUILD_TESTING) set_property(TEST MSVCDebugInformationFormat APPEND PROPERTY LABELS "CUDA" "Fortran") + set(MSVCRuntimeChecks_BUILD_OPTIONS -DCMake_TEST_CUDA=${CMake_TEST_CUDA}) + if(CMAKE_Fortran_COMPILER) + list(APPEND MSVCRuntimeChecks_BUILD_OPTIONS -DCMake_TEST_Fortran=1) + endif() + ADD_TEST_MACRO(MSVCRuntimeChecks) + set_property(TEST MSVCRuntimeChecks APPEND + PROPERTY LABELS "CUDA" "Fortran") + set(MSVCRuntimeLibrary_BUILD_OPTIONS -DCMake_TEST_CUDA=${CMake_TEST_CUDA}) ADD_TEST_MACRO(MSVCRuntimeLibrary) set_property(TEST MSVCRuntimeLibrary APPEND diff --git a/Tests/FortranOnly/CMakeLists.txt b/Tests/FortranOnly/CMakeLists.txt index c9f89cb326..4f2724deb1 100644 --- a/Tests/FortranOnly/CMakeLists.txt +++ b/Tests/FortranOnly/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.10...3.25) # Enable CMP0091 and CMP0141. +cmake_policy(SET CMP0184 NEW) project(FortranOnly Fortran) message("CTEST_FULL_OUTPUT ") diff --git a/Tests/MSVCRuntimeChecks/CMakeLists.txt b/Tests/MSVCRuntimeChecks/CMakeLists.txt new file mode 100644 index 0000000000..cac117c4e2 --- /dev/null +++ b/Tests/MSVCRuntimeChecks/CMakeLists.txt @@ -0,0 +1,105 @@ +cmake_minimum_required(VERSION 3.31) +cmake_policy(SET CMP0184 NEW) + +# All runtime checks flags enables single preprocessor definition, +# so override our table of flags to artificially add a definition we can check. +set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/override-C.cmake) +set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/override-CXX.cmake) +set(CMAKE_USER_MAKE_RULES_OVERRIDE_CUDA ${CMAKE_CURRENT_SOURCE_DIR}/override-CUDA.cmake) +set(CMAKE_USER_MAKE_RULES_OVERRIDE_Fortran ${CMAKE_CURRENT_SOURCE_DIR}/override-Fortran.cmake) + +project(MSVCRuntimeChecks) +if(CMake_TEST_CUDA STREQUAL "NVIDIA") + enable_language(CUDA) +endif() +if(CMake_TEST_Fortran) + enable_language(Fortran) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +set(verify_default VERIFY_RTCsu) + +set(verify_def_PossibleDataLoss -DVERIFY_RTCc) +set(verify_def_StackFrameErrorCheck -DVERIFY_RTCs) +set(verify_def_UninitializedVariable -DVERIFY_RTCu) +set(verify_def_RTCsu -DVERIFY_RTCsu) + +function(verify_combination format verify_format_defs lang src) + # Test that try_compile builds with this runtime check. + set(CMAKE_MSVC_RUNTIME_CHECKS "${format}") + set(CMAKE_TRY_COMPILE_CONFIGURATION "Debug") + set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") + + string(REPLACE ";" "_" format_var_name "${format}") + if (NOT format_var_name) + set(format_var_name "Empty") + endif() + + if (NOT verify_format_defs) + foreach(format_for_def IN LISTS format) + list(APPEND verify_format_defs ${verify_def_${format_for_def}}) + endforeach() + endif() + + try_compile(${format_var_name}_COMPILES + ${CMAKE_CURRENT_BINARY_DIR}/try_compile/${format_var_name} + ${CMAKE_CURRENT_SOURCE_DIR}/${src} + COMPILE_DEFINITIONS ${verify_format_defs} + CMAKE_FLAGS -DINCLUDE_DIRECTORIES=${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE ${format_var_name}_OUTPUT + ) + if(${format_var_name}_COMPILES) + message(STATUS "try_compile ${lang} with \"${format}\" worked") + else() + string(REPLACE "\n" "\n " ${format_var_name}_OUTPUT " ${${format_var_name}_OUTPUT}") + message(SEND_ERROR "try_compile ${lang} with \"${format}\" failed:\n${${format_var_name}_OUTPUT}") + endif() + + # Test that targets build with this runtime check. + set(CMAKE_MSVC_RUNTIME_CHECKS "$<$>:${format}>$<$>:BadContent>") + add_library(${format_var_name}-${lang} ${src}) + set_property(TARGET ${format_var_name}-${lang} PROPERTY BOOL_TRUE TRUE) + target_compile_definitions(${format_var_name}-${lang} PRIVATE ${verify_format_defs}) +endfunction() + +function(verify lang src) + add_library(default-${lang} ${src}) + target_compile_definitions(default-${lang} PRIVATE "$<$:${verify_default}>") + + # zero checkers + verify_combination("" "" ${lang} ${src}) + + # single checker + verify_combination(PossibleDataLoss "" ${lang} ${src}) + verify_combination(StackFrameErrorCheck "" ${lang} ${src}) + verify_combination(UninitializedVariable "" ${lang} ${src}) + verify_combination(RTCsu "" ${lang} ${src}) + + # multiple checkers (without RTCsu merging) + verify_combination("PossibleDataLoss;StackFrameErrorCheck" "" ${lang} ${src}) + verify_combination("PossibleDataLoss;UninitializedVariable" "" ${lang} ${src}) + + # multiple checkers (only RTCsu merging) + set(defs "${verify_def_RTCsu}") + verify_combination("StackFrameErrorCheck;UninitializedVariable" "${defs}" ${lang} ${src}) + verify_combination("StackFrameErrorCheck;RTCsu" "${defs}" ${lang} ${src}) + verify_combination("UninitializedVariable;RTCsu" "${defs}" ${lang} ${src}) + verify_combination("StackFrameErrorCheck;UninitializedVariable;RTCsu" "${defs}" ${lang} ${src}) + + # multiple checkers (with RTCsu merging) + list(APPEND defs "${verify_def_PossibleDataLoss}") + verify_combination("PossibleDataLoss;StackFrameErrorCheck;UninitializedVariable" "${defs}" ${lang} ${src}) + verify_combination("PossibleDataLoss;StackFrameErrorCheck;RTCsu" "${defs}" ${lang} ${src}) + verify_combination("PossibleDataLoss;UninitializedVariable;RTCsu" "${defs}" ${lang} ${src}) + verify_combination("PossibleDataLoss;StackFrameErrorCheck;UninitializedVariable;RTCsu" "${defs}" ${lang} ${src}) +endfunction() + +verify(C verify.c) +verify(CXX verify.cxx) +if(CMake_TEST_CUDA STREQUAL "NVIDIA") + verify(CUDA verify.cu) +endif() +if(CMake_TEST_Fortran) + verify(Fortran verify.F90) +endif() diff --git a/Tests/MSVCRuntimeChecks/override-C.cmake b/Tests/MSVCRuntimeChecks/override-C.cmake new file mode 100644 index 0000000000..8e25994610 --- /dev/null +++ b/Tests/MSVCRuntimeChecks/override-C.cmake @@ -0,0 +1,3 @@ +set(delimiter ";") +set(lang "C") +include(${CMAKE_CURRENT_LIST_DIR}/override-common.cmake) diff --git a/Tests/MSVCRuntimeChecks/override-CUDA.cmake b/Tests/MSVCRuntimeChecks/override-CUDA.cmake new file mode 100644 index 0000000000..7f61309bf7 --- /dev/null +++ b/Tests/MSVCRuntimeChecks/override-CUDA.cmake @@ -0,0 +1,3 @@ +set(delimiter " ") +set(lang "CUDA") +include(${CMAKE_CURRENT_LIST_DIR}/override-common.cmake) diff --git a/Tests/MSVCRuntimeChecks/override-CXX.cmake b/Tests/MSVCRuntimeChecks/override-CXX.cmake new file mode 100644 index 0000000000..27e1579185 --- /dev/null +++ b/Tests/MSVCRuntimeChecks/override-CXX.cmake @@ -0,0 +1,3 @@ +set(delimiter ";") +set(lang "CXX") +include(${CMAKE_CURRENT_LIST_DIR}/override-common.cmake) diff --git a/Tests/MSVCRuntimeChecks/override-Fortran.cmake b/Tests/MSVCRuntimeChecks/override-Fortran.cmake new file mode 100644 index 0000000000..0a790ebebb --- /dev/null +++ b/Tests/MSVCRuntimeChecks/override-Fortran.cmake @@ -0,0 +1,3 @@ +set(delimiter ";") +set(lang "Fortran") +include(${CMAKE_CURRENT_LIST_DIR}/override-common.cmake) diff --git a/Tests/MSVCRuntimeChecks/override-common.cmake b/Tests/MSVCRuntimeChecks/override-common.cmake new file mode 100644 index 0000000000..024170232a --- /dev/null +++ b/Tests/MSVCRuntimeChecks/override-common.cmake @@ -0,0 +1,50 @@ +if("${CMAKE_${lang}_COMPILER_ID};${CMAKE_${lang}_SIMULATE_ID};${CMAKE_${lang}_COMPILER_FRONTEND_VARIANT}" STREQUAL "Clang;MSVC;GNU") + # Clang does not actually support these, so Windows-Clang passes no flags. + set(empty_PossibleDataLoss 1) + set(empty_StackFrameErrorCheck 1) + set(empty_UninitializedVariable 1) + set(empty_RTCsu 1) +elseif("${CMAKE_${lang}_COMPILER_ID};${CMAKE_${lang}_SIMULATE_ID};${lang}" MATCHES "^Intel(LLVM)?;MSVC;Fortran$") + # IntelLLVM Fortran does not actually support these, so Windows-IntelLLVM-Fortran passes no flags. + set(empty_PossibleDataLoss 1) + set(empty_StackFrameErrorCheck 1) + set(empty_UninitializedVariable 0) # this one is supported + set(empty_RTCsu 1) +elseif(CMAKE_${lang}_COMPILER_ID STREQUAL "MSVC" AND CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 14) + set(empty_PossibleDataLoss 1) + set(empty_StackFrameErrorCheck 0) + set(empty_UninitializedVariable 1) + set(empty_RTCsu 0) +else() + set(empty_PossibleDataLoss 0) + set(empty_StackFrameErrorCheck 0) + set(empty_UninitializedVariable 0) + set(empty_RTCsu 0) +endif() + +set(var "CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss") +if(empty_PossibleDataLoss AND ${var} STREQUAL "") + set("${var}" "-DTEST_RTCc") +else() + string(REPLACE "-RTCc" "-RTCc${delimiter}-DTEST_RTCc" "${var}" "${${var}}") +endif() +set(var "CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck") +if(empty_StackFrameErrorCheck AND ${var} STREQUAL "") + set("${var}" "-DTEST_RTCs") +else() + string(REPLACE "-RTCs" "-RTCs${delimiter}-DTEST_RTCs" "${var}" "${${var}}") + string(REPLACE "-GZ" "-GZ${delimiter}-DTEST_RTCs" "${var}" "${${var}}") +endif() +set(var "CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable") +if(empty_UninitializedVariable AND ${var} STREQUAL "") + set("${var}" "-DTEST_RTCu") +else() + string(REPLACE "-RTCu" "-RTCu${delimiter}-DTEST_RTCu" "${var}" "${${var}}") +endif() +set(var "CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu") +if(empty_RTCsu AND ${var} STREQUAL "") + set("${var}" "-DTEST_RTCsu") +else() + string(REPLACE "-RTC1" "-RTC1${delimiter}-DTEST_RTCsu" "${var}" "${${var}}") + string(REPLACE "-GZ" "-GZ${delimiter}-DTEST_RTCsu" "${var}" "${${var}}") +endif() diff --git a/Tests/MSVCRuntimeChecks/verify.F90 b/Tests/MSVCRuntimeChecks/verify.F90 new file mode 100644 index 0000000000..741bca6b89 --- /dev/null +++ b/Tests/MSVCRuntimeChecks/verify.F90 @@ -0,0 +1 @@ +#include "verify.h" diff --git a/Tests/MSVCRuntimeChecks/verify.c b/Tests/MSVCRuntimeChecks/verify.c new file mode 100644 index 0000000000..741bca6b89 --- /dev/null +++ b/Tests/MSVCRuntimeChecks/verify.c @@ -0,0 +1 @@ +#include "verify.h" diff --git a/Tests/MSVCRuntimeChecks/verify.cu b/Tests/MSVCRuntimeChecks/verify.cu new file mode 100644 index 0000000000..741bca6b89 --- /dev/null +++ b/Tests/MSVCRuntimeChecks/verify.cu @@ -0,0 +1 @@ +#include "verify.h" diff --git a/Tests/MSVCRuntimeChecks/verify.cxx b/Tests/MSVCRuntimeChecks/verify.cxx new file mode 100644 index 0000000000..741bca6b89 --- /dev/null +++ b/Tests/MSVCRuntimeChecks/verify.cxx @@ -0,0 +1 @@ +#include "verify.h" diff --git a/Tests/MSVCRuntimeChecks/verify.h b/Tests/MSVCRuntimeChecks/verify.h new file mode 100644 index 0000000000..aae595b98c --- /dev/null +++ b/Tests/MSVCRuntimeChecks/verify.h @@ -0,0 +1,61 @@ +/* Some compilers ignore the -RTC flags even if specified. */ +#if (defined(_MSC_VER) && _MSC_VER <= 1310) || defined(__clang__) || \ + defined(__INTEL_LLVM_COMPILER) || defined(__INTEL_COMPILER) +# define NO__MSVC_RUNTIME_CHECKS +#endif + +#ifdef VERIFY_RTCc +# ifndef TEST_RTCc +# error "TEST_RTCc incorrectly not defined by enabled runtime check" +# endif +# if !defined(__MSVC_RUNTIME_CHECKS) && !defined(NO__MSVC_RUNTIME_CHECKS) +# error \ + "__MSVC_RUNTIME_CHECKS incorrectly not defined by enabled RTCc runtime check" +# endif +#else +# ifdef TEST_RTCc +# error "TEST_RTCc incorrectly defined by disabled runtime check" +# endif +#endif + +#ifdef VERIFY_RTCs +# ifndef TEST_RTCs +# error "TEST_RTCs incorrectly not defined by enabled runtime check" +# endif +# if !defined(__MSVC_RUNTIME_CHECKS) && !defined(NO__MSVC_RUNTIME_CHECKS) +# error \ + "__MSVC_RUNTIME_CHECKS incorrectly not defined by enabled RTCs runtime check" +# endif +#else +# ifdef TEST_RTCs +# error "TEST_RTCs incorrectly defined by disabled runtime check" +# endif +#endif + +#ifdef VERIFY_RTCu +# ifndef TEST_RTCu +# error "TEST_RTCu incorrectly not defined by enabled runtime check" +# endif +# if !defined(__MSVC_RUNTIME_CHECKS) && !defined(NO__MSVC_RUNTIME_CHECKS) +# error \ + "__MSVC_RUNTIME_CHECKS incorrectly not defined by enabled RTCu runtime check" +# endif +#else +# ifdef TEST_RTCu +# error "TEST_RTCu incorrectly defined by disabled runtime check" +# endif +#endif + +#ifdef VERIFY_RTCsu +# ifndef TEST_RTCsu +# error "TEST_RTCsu incorrectly not defined by enabled runtime check" +# endif +# if !defined(__MSVC_RUNTIME_CHECKS) && !defined(NO__MSVC_RUNTIME_CHECKS) +# error \ + "__MSVC_RUNTIME_CHECKS incorrectly not defined by enabled RTCsu runtime check" +# endif +#else +# ifdef TEST_RTCsu +# error "TEST_RTCsu incorrectly defined by disabled runtime check" +# endif +#endif diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 5913c807e8..1bb57eaa21 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -483,6 +483,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|Fujitsu|F add_RunCMake_test(MetaCompileFeatures) endif() if(MSVC) + add_RunCMake_test(MSVCRuntimeChecks) add_RunCMake_test(MSVCRuntimeLibrary) add_RunCMake_test(MSVCRuntimeTypeInfo) add_RunCMake_test(MSVCWarningFlags) diff --git a/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-result.txt b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-stderr.txt b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-stderr.txt new file mode 100644 index 0000000000..0fc2135b1e --- /dev/null +++ b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error in CMakeLists.txt: + MSVC_RUNTIME_CHECKS value 'BogusValue' not known for this C compiler. ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$ diff --git a/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW.cmake b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW.cmake new file mode 100644 index 0000000000..0f0d3c302f --- /dev/null +++ b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0184 NEW) +include(CMP0184-common.cmake) diff --git a/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NoEffect.cmake b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NoEffect.cmake new file mode 100644 index 0000000000..13244b13ba --- /dev/null +++ b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NoEffect.cmake @@ -0,0 +1,4 @@ +include(CMP0184-common.cmake) + +# Setting this policy after enable_language command has no effect. +cmake_policy(SET CMP0184 NEW) diff --git a/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-OLD.cmake b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-OLD.cmake new file mode 100644 index 0000000000..32dc4ffa34 --- /dev/null +++ b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0184 OLD) +include(CMP0184-common.cmake) diff --git a/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-WARN.cmake b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-WARN.cmake new file mode 100644 index 0000000000..7e960abfb0 --- /dev/null +++ b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-WARN.cmake @@ -0,0 +1,2 @@ + +include(CMP0184-common.cmake) diff --git a/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-common.cmake b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-common.cmake new file mode 100644 index 0000000000..c3ca394d19 --- /dev/null +++ b/Tests/RunCMake/MSVCRuntimeChecks/CMP0184-common.cmake @@ -0,0 +1,25 @@ +enable_language(C) + +cmake_policy(GET CMP0184 cmp0184) +if(cmp0184 STREQUAL "NEW") + if(NOT CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT) + message(SEND_ERROR "CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT not set under NEW behavior") + endif() +else() + if(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT) + message(SEND_ERROR "CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT is set under OLD behavior") + endif() +endif() + +if(cmp0184 STREQUAL "NEW") + if(CMAKE_C_FLAGS_DEBUG MATCHES "[/-](RTC1|GZ)( |$)") + message(SEND_ERROR "CMAKE_C_FLAGS_DEBUG has -RTC1 flag under NEW behavior:\n ${CMAKE_C_FLAGS_DEBUG}") + endif() +else() + if(NOT (CMAKE_C_FLAGS_DEBUG MATCHES "/(RTC1|GZ)( |$)")) + message(SEND_ERROR "CMAKE_C_FLAGS_DEBUG does not have /RTC1 flag under OLD behavior:\n ${CMAKE_C_FLAGS_DEBUG}") + endif() +endif() + +set(CMAKE_MSVC_RUNTIME_CHECKS BogusValue) +add_library(foo empty.c) diff --git a/Tests/RunCMake/MSVCRuntimeChecks/CMakeLists.txt b/Tests/RunCMake/MSVCRuntimeChecks/CMakeLists.txt new file mode 100644 index 0000000000..b7814f25e0 --- /dev/null +++ b/Tests/RunCMake/MSVCRuntimeChecks/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.31) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/MSVCRuntimeChecks/RunCMakeTest.cmake b/Tests/RunCMake/MSVCRuntimeChecks/RunCMakeTest.cmake new file mode 100644 index 0000000000..a2fe769c1b --- /dev/null +++ b/Tests/RunCMake/MSVCRuntimeChecks/RunCMakeTest.cmake @@ -0,0 +1,6 @@ +include(RunCMake) + +run_cmake(CMP0184-WARN) +run_cmake(CMP0184-OLD) +run_cmake(CMP0184-NEW) +run_cmake(CMP0184-NoEffect) diff --git a/Tests/RunCMake/MSVCRuntimeChecks/empty.c b/Tests/RunCMake/MSVCRuntimeChecks/empty.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Tests/VSMARMASM/CMakeLists.txt b/Tests/VSMARMASM/CMakeLists.txt index 749846139d..8f27d54c4c 100644 --- a/Tests/VSMARMASM/CMakeLists.txt +++ b/Tests/VSMARMASM/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.25) # Enable CMP0141 +cmake_policy(SET CMP0184 NEW) project(VSMARMASM C ASM_MARMASM) add_executable(VSMARMASM main.c foo.asm) target_compile_options(VSMARMASM PRIVATE diff --git a/Tests/VSMASM/CMakeLists.txt b/Tests/VSMASM/CMakeLists.txt index bf9a4b85f9..03bb7ac8fb 100644 --- a/Tests/VSMASM/CMakeLists.txt +++ b/Tests/VSMASM/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.25) # Enable CMP0141 +cmake_policy(SET CMP0184 NEW) project(VSMASM C ASM_MASM) if(CMAKE_SIZEOF_VOID_P EQUAL 8) add_definitions(-DTESTx64)