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
This commit is contained in:
Joerg Bornemann
2026-09-18 11:35:02 +02:00
parent 0fd711348c
commit 7c3ca57b91
4 changed files with 53 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
// An empty translation unit for CMAKE_<LANG>_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.
+25
View File
@@ -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)
+17
View File
@@ -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()
@@ -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()