From 7c3ca57b9102b475daa890a90d8f161add68ad4a Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Fri, 18 Sep 2026 11:35:02 +0200 Subject: [PATCH] MSVC,Clang: Set CMAKE_CXX_COMPILER_PREDEFINES_COMMAND for the MSVC ABI The variable was only set by the GNU-like compiler modules, so AUTOMOC never generated a moc_predefs.h for compilers targeting the MSVC ABI and moc parsed headers without the compiler's predefined macros. MSVC dumps its predefined macros with '/PD', which needs the conforming preprocessor. Require VS 2019 16.8, because 16.7 and older ignore the flag with warning D9002 and emit no macros at all. clang-cl takes '-dM' through '-Xclang'. Both also echo the preprocessed source, so feed them an empty translation unit. clang with the GNU driver is excluded from 'Compiler/GNU' as well when it simulates MSVC, so give it the usual '-dM -E' command there. Fixes: #28093 --- Modules/CMakeCXXCompilerPredefines.cpp | 3 +++ Modules/Compiler/Clang-CXX.cmake | 25 +++++++++++++++++++++++ Modules/Compiler/MSVC.cmake | 17 +++++++++++++++ Tests/RunCMake/Autogen_1/MocPredefs.cmake | 8 ++++++++ 4 files changed, 53 insertions(+) create mode 100644 Modules/CMakeCXXCompilerPredefines.cpp diff --git a/Modules/CMakeCXXCompilerPredefines.cpp b/Modules/CMakeCXXCompilerPredefines.cpp new file mode 100644 index 0000000000..418a8d1e32 --- /dev/null +++ b/Modules/CMakeCXXCompilerPredefines.cpp @@ -0,0 +1,3 @@ +// An empty translation unit for CMAKE__COMPILER_PREDEFINES_COMMAND. +// MSVC-like compilers echo the preprocessed source along with the macro +// definitions, so the input must contribute no output of its own. diff --git a/Modules/Compiler/Clang-CXX.cmake b/Modules/Compiler/Clang-CXX.cmake index 5a8bc3fda3..d0c3228c79 100644 --- a/Modules/Compiler/Clang-CXX.cmake +++ b/Modules/Compiler/Clang-CXX.cmake @@ -20,6 +20,31 @@ if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC") set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl") endif() +# 'Compiler/GNU' is not included when clang simulates MSVC, so the predefines +# command is not set up there. +if(NOT DEFINED CMAKE_CXX_COMPILER_PREDEFINES_COMMAND) + set(CMAKE_CXX_COMPILER_PREDEFINES_COMMAND "${CMAKE_CXX_COMPILER}") + if(CMAKE_CXX_COMPILER_ARG1) + separate_arguments(_COMPILER_ARGS NATIVE_COMMAND "${CMAKE_CXX_COMPILER_ARG1}") + list(APPEND CMAKE_CXX_COMPILER_PREDEFINES_COMMAND ${_COMPILER_ARGS}) + unset(_COMPILER_ARGS) + endif() + if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC") + # '-dM' is a cc1 option in this driver mode, and '-EP' also echoes the + # preprocessed source, hence the empty translation unit. + list(APPEND CMAKE_CXX_COMPILER_PREDEFINES_COMMAND + "-nologo" "-w" "-Xclang" "-dM" "-EP" + "${CMAKE_ROOT}/Modules/CMakeCXXCompilerPredefines.cpp") + else() + list(APPEND CMAKE_CXX_COMPILER_PREDEFINES_COMMAND + "-w" "-dM" "-E" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp") + endif() + if(CMAKE_CXX_COMPILER_TARGET) + list(APPEND CMAKE_CXX_COMPILER_PREDEFINES_COMMAND + "--target=${CMAKE_CXX_COMPILER_TARGET}") + endif() +endif() + if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0 AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.1 AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")) if (CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR) diff --git a/Modules/Compiler/MSVC.cmake b/Modules/Compiler/MSVC.cmake index 40517b93e2..35c67e4170 100644 --- a/Modules/Compiler/MSVC.cmake +++ b/Modules/Compiler/MSVC.cmake @@ -23,5 +23,22 @@ macro(__compiler_msvc lang) set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang}_WARNING "-external:W0 ") endif () + # '/PD' dumps the predefined macros, but it needs the conforming + # preprocessor and one of '/E', '/EP' or '/P'. VS 2019 16.7 and older + # ignore it with warning D9002, so require 16.8. '/EP' also echoes the + # preprocessed source, hence the empty translation unit. + if("${lang}" STREQUAL "CXX" AND + CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 19.28) + set(CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "${CMAKE_${lang}_COMPILER}") + if(CMAKE_${lang}_COMPILER_ARG1) + separate_arguments(_COMPILER_ARGS NATIVE_COMMAND "${CMAKE_${lang}_COMPILER_ARG1}") + list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND ${_COMPILER_ARGS}) + unset(_COMPILER_ARGS) + endif() + list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND + "-nologo" "-w" "-Zc:preprocessor" "-PD" "-EP" + "${CMAKE_ROOT}/Modules/CMakeCXXCompilerPredefines.cpp") + endif() + set(CMAKE_${lang}_LINK_MODE LINKER) endmacro() diff --git a/Tests/RunCMake/Autogen_1/MocPredefs.cmake b/Tests/RunCMake/Autogen_1/MocPredefs.cmake index 6ea192a273..cc71119f8e 100644 --- a/Tests/RunCMake/Autogen_1/MocPredefs.cmake +++ b/Tests/RunCMake/Autogen_1/MocPredefs.cmake @@ -11,6 +11,14 @@ set(CMAKE_CXX_EXTENSIONS OFF) add_library(MocPredefs MocPredefs.cxx) if(NOT DEFINED CMAKE_CXX_COMPILER_PREDEFINES_COMMAND) + # Do not silently skip on toolchains that are expected to provide it. + if((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER_EQUAL 1928) + OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang" + AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")) + message(FATAL_ERROR + "CMAKE_CXX_COMPILER_PREDEFINES_COMMAND is not defined for " + "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}.") + endif() return() endif()