PCH: Add PCH_WARNINGS property to get warnings from precompiled headers

The generated precompiled header is marked as a system header, which
suppresses the diagnostics of every header it includes.  Enabling
precompiled headers therefore silently hides warnings that the same
build
would produce without them.

Move the pragma out of CMAKE_PCH_PROLOGUE into its own
CMAKE_PCH_SYSTEM_HEADER_PRAGMA variable so that it can be omitted
independently of the user-facing prologue and epilogue, and add a
PCH_WARNINGS target property, initialized by CMAKE_PCH_WARNINGS, to omit
it.  The diagnostics are then reported once, while compiling the
precompiled header itself.

Fixes: #21219
This commit is contained in:
Cristian Adam committed 2026-10-14 07:32:04 +02:00
1 parent 073a48e11f
commit cec0ec5273
20 files changed
+141 -9

No files matched your search

@@ -127,6 +127,9 @@ See Also
* To prevent precompile headers from being used when compiling a specific
source file, see the :prop_sf:`SKIP_PRECOMPILE_HEADERS` source file property.
* To get compiler warnings for the precompiled headers themselves, which are
suppressed by default, see the :prop_tgt:`PCH_WARNINGS` target property.
* :command:`target_compile_definitions`
* :command:`target_compile_features`
* :command:`target_compile_options`
+1
View File
@@ -428,6 +428,7 @@ Properties on Targets
/prop_tgt/OUTPUT_NAME
/prop_tgt/OUTPUT_NAME_CONFIG
/prop_tgt/PCH_INSTANTIATE_TEMPLATES
/prop_tgt/PCH_WARNINGS
/prop_tgt/PCH_WARN_INVALID
/prop_tgt/PDB_NAME
/prop_tgt/PDB_NAME_CONFIG
+1
View File
@@ -572,6 +572,7 @@ Variables that Control the Build
/variable/CMAKE_OSX_DEPLOYMENT_TARGET
/variable/CMAKE_OSX_SYSROOT
/variable/CMAKE_PCH_INSTANTIATE_TEMPLATES
/variable/CMAKE_PCH_WARNINGS
/variable/CMAKE_PCH_WARN_INVALID
/variable/CMAKE_PDB_OUTPUT_DIRECTORY
/variable/CMAKE_PDB_OUTPUT_DIRECTORY_CONFIG
+27
View File
@@ -0,0 +1,27 @@
PCH_WARNINGS
------------
.. versionadded:: 4.6
Enable compiler warnings for the headers listed in :prop_tgt:`PRECOMPILE_HEADERS`.
By default CMake marks the generated precompiled header file as a system
header, which suppresses the diagnostics of every header it includes. This
means that enabling precompiled headers silently hides warnings that the same
build would produce without them.
Set this property to true to omit the system header marker so that the headers
collected into the precompiled header are diagnosed as they would be in a
build without precompiled headers. The diagnostics are reported once, while
compiling the precompiled header itself, rather than once per source file.
This property is initialized by the value of the :variable:`CMAKE_PCH_WARNINGS`
variable when the target is created.
The :generator:`Xcode` generator never adds the system header marker, so this
property has no effect there.
Targets that reuse a precompiled header through
:prop_tgt:`PRECOMPILE_HEADERS_REUSE_FROM` do not generate a precompiled header
of their own, so this property has no effect on them either. Set it on the
target named by :prop_tgt:`PRECOMPILE_HEADERS_REUSE_FROM` instead.
+15
View File
@@ -0,0 +1,15 @@
pch-warnings
------------
* The :prop_tgt:`PCH_WARNINGS` target property, and its initializing
:variable:`CMAKE_PCH_WARNINGS` variable, were added to enable compiler
warnings for the headers collected into a precompiled header. Previously
the generated precompiled header was always marked as a system header,
which suppressed those warnings.
The system header marker now comes from an internal
``CMAKE_PCH_SYSTEM_HEADER_PRAGMA`` variable instead of from
``CMAKE_PCH_PROLOGUE``. Projects that cleared or replaced
``CMAKE_PCH_PROLOGUE`` in order to drop the marker no longer affect it,
and must set :prop_tgt:`PCH_WARNINGS` or :variable:`CMAKE_PCH_WARNINGS`
instead.
+7
View File
@@ -0,0 +1,7 @@
CMAKE_PCH_WARNINGS
------------------
.. versionadded:: 4.6
This variable is used to initialize the :prop_tgt:`PCH_WARNINGS`
property of targets when they are created.
+1 -1
View File
@@ -109,7 +109,7 @@ else()
set(CMAKE_PCH_EXTENSION .pch)
if (NOT CMAKE_GENERATOR MATCHES "Xcode")
set(CMAKE_PCH_PROLOGUE "#pragma clang system_header")
set(CMAKE_PCH_SYSTEM_HEADER_PRAGMA "#pragma clang system_header")
endif()
if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0.0 AND NOT __is_apple_clang)
set(CMAKE_${lang}_COMPILE_OPTIONS_INSTANTIATE_TEMPLATES_PCH -fpch-instantiate-templates)
+1 -1
View File
@@ -142,7 +142,7 @@ macro(__compiler_gnu lang)
if(NOT "x${lang}" STREQUAL "xFortran")
set(CMAKE_PCH_EXTENSION .gch)
if (NOT CMAKE_GENERATOR MATCHES "Xcode")
set(CMAKE_PCH_PROLOGUE "#pragma GCC system_header")
set(CMAKE_PCH_SYSTEM_HEADER_PRAGMA "#pragma GCC system_header")
endif()
set(CMAKE_${lang}_COMPILE_OPTIONS_INVALID_PCH -Winvalid-pch)
set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -include <PCH_HEADER>)
+3 -1
View File
@@ -115,7 +115,9 @@ else()
else()
# Precompile Headers
set(CMAKE_PCH_EXTENSION .pch)
set(CMAKE_PCH_PROLOGUE "#pragma clang system_header")
if (NOT CMAKE_GENERATOR MATCHES "Xcode")
set(CMAKE_PCH_SYSTEM_HEADER_PRAGMA "#pragma clang system_header")
endif()
set(CMAKE_${lang}_COMPILE_OPTIONS_INSTANTIATE_TEMPLATES_PCH -fpch-instantiate-templates)
set(CMAKE_${lang}_COMPILE_OPTIONS_INVALID_PCH -Winvalid-pch)
set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Xclang -include-pch -Xclang <PCH_FILE> -Xclang -include -Xclang <PCH_HEADER>)
+1 -1
View File
@@ -91,7 +91,7 @@ macro(__compiler_lcc lang)
if(NOT "x${lang}" STREQUAL "xFortran")
set(CMAKE_PCH_EXTENSION .gch)
if (NOT CMAKE_GENERATOR MATCHES "Xcode")
set(CMAKE_PCH_PROLOGUE "#pragma GCC system_header")
set(CMAKE_PCH_SYSTEM_HEADER_PRAGMA "#pragma GCC system_header")
endif()
set(CMAKE_${lang}_COMPILE_OPTIONS_INVALID_PCH -Winvalid-pch)
set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -include <PCH_HEADER>)
+1 -1
View File
@@ -143,7 +143,7 @@ macro(__windows_compiler_clang_gnu lang)
set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON)
set(CMAKE_PCH_EXTENSION .pch)
set(CMAKE_PCH_PROLOGUE "#pragma clang system_header")
set(CMAKE_PCH_SYSTEM_HEADER_PRAGMA "#pragma clang system_header")
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}})
+2 -2
View File
@@ -446,7 +446,7 @@ macro(__windows_compiler_msvc lang)
set(CMAKE_PCH_EXTENSION .pch)
set(CMAKE_LINK_PCH ON)
if (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang")
set(CMAKE_PCH_PROLOGUE "#pragma clang system_header")
set(CMAKE_PCH_SYSTEM_HEADER_PRAGMA "#pragma clang system_header")
# macOS paths usually start with /Users/*. Unfortunately, clang-cl interprets
# paths starting with /U as macro undefines, so we need to put a -- before the
@@ -458,7 +458,7 @@ macro(__windows_compiler_msvc lang)
endif()
elseif(MSVC_VERSION GREATER_EQUAL 1913)
# At least MSVC toolet 14.13 from VS 2017 15.6
set(CMAKE_PCH_PROLOGUE "#pragma system_header")
set(CMAKE_PCH_SYSTEM_HEADER_PRAGMA "#pragma system_header")
endif()
if (NOT ${CMAKE_${lang}_COMPILER_ID} STREQUAL "Clang")
set(CMAKE_PCH_COPY_COMPILE_PDB ON)
+2
View File
@@ -497,6 +497,8 @@ Entry const kEntries[] = {
"Location or short name (e.g. macosx) of the macOS platform SDK passed to the compiler via -isysroot. Initialized from the SDKROOT environment variable; empty by default."_s },
{ "CMAKE_PCH_INSTANTIATE_TEMPLATES"_s,
"Initializes the PCH_INSTANTIATE_TEMPLATES target property"_s },
{ "CMAKE_PCH_WARNINGS"_s,
"Initializes the PCH_WARNINGS target property"_s },
{ "CMAKE_PCH_WARN_INVALID"_s,
"Initializes the PCH_WARN_INVALID target property"_s },
{ "CMAKE_PDB_OUTPUT_DIRECTORY"_s,
+15 -1
View File
@@ -3359,6 +3359,12 @@ std::string cmGeneratorTarget::GetPchHeader(std::string const& config,
std::string const filename_tmp = cmStrCat(filename, ".tmp");
if (!haveReuseTarget) {
// Marking the generated header as a system header suppresses the
// diagnostics of every header it includes. Skip it when the target
// asks for warnings from its precompiled headers.
cmValue pchSystemHeaderPragma = this->GetPropertyAsBool("PCH_WARNINGS")
? nullptr
: this->Makefile->GetDefinition("CMAKE_PCH_SYSTEM_HEADER_PRAGMA");
cmValue pchPrologue =
this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
cmValue pchEpilogue =
@@ -3370,6 +3376,9 @@ std::string cmGeneratorTarget::GetPchHeader(std::string const& config,
filename_tmp, false,
this->GetGlobalGenerator()->GetMakefileEncoding());
file << "/* generated by CMake */\n\n";
if (pchSystemHeaderPragma) {
file << *pchSystemHeaderPragma << "\n";
}
if (pchPrologue) {
file << *pchPrologue << "\n";
}
@@ -3405,7 +3414,12 @@ std::string cmGeneratorTarget::GetPchHeader(std::string const& config,
}
}
if (!firstHeaderOnDisk.empty()) {
// Give the generated header the timestamp of a header from the project
// to help ccache. Do this only when creating it for the first time:
// if its content later changes, e.g. because PCH_WARNINGS was toggled,
// an older timestamp would make build systems consider the already
// precompiled header up to date.
if (!firstHeaderOnDisk.empty() && !cmSystemTools::FileExists(filename)) {
cmFileTimes::Copy(firstHeaderOnDisk, filename_tmp);
}
+1
View File
@@ -339,6 +339,7 @@ TargetProperty const StaticTargetProperties[] = {
{ "DISABLE_PRECOMPILE_HEADERS"_s, IC::CanCompileSources },
{ "PCH_WARN_INVALID"_s, "ON"_s, IC::CanCompileSources },
{ "PCH_INSTANTIATE_TEMPLATES"_s, "ON"_s, IC::CanCompileSources },
{ "PCH_WARNINGS"_s, IC::CanCompileSources },
// -- Platforms
// ---- Android
{ "ANDROID_API"_s, IC::CanCompileSources },
+2 -1
View File
@@ -1666,7 +1666,8 @@ add_RunCMake_test("CTestCommandExpandLists")
add_RunCMake_test(PrecompileHeaders -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
-DCMAKE_C_SIMULATE_ID=${CMAKE_C_SIMULATE_ID}
-DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION})
-DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}
-DMSVC_VERSION=${MSVC_VERSION})
add_RunCMake_test(PrecompileHeaders-Reuse -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
# This test can take a long time due to the number of test cases.
@@ -0,0 +1,40 @@
if (RunCMake_GENERATOR STREQUAL "Xcode" OR
NOT CMAKE_C_COMPILER_ID MATCHES "^(GNU|LCC|Clang|AppleClang|IntelLLVM|MSVC)$")
return()
endif()
# MSVC defines the system header pragma only for toolset 14.13 from VS 2017 15.6.
if (CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION LESS 1913)
return()
endif()
function(check_system_header_pragma target expected)
set(pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/${target}.dir/cmake_pch.h")
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/${target}.dir/Debug/cmake_pch.h")
endif()
if (NOT EXISTS "${pch_header}")
set(RunCMake_TEST_FAILED
"Generated pch file for '${target}'\n ${pch_header}\ndoes not exist"
PARENT_SCOPE)
return()
endif()
file(STRINGS "${pch_header}" pch_header_strings REGEX "system_header")
if (expected AND NOT pch_header_strings)
set(RunCMake_TEST_FAILED
"Generated pch file for '${target}' is missing the system header pragma"
PARENT_SCOPE)
elseif (NOT expected AND pch_header_strings)
set(RunCMake_TEST_FAILED
"Generated pch file for '${target}' has an unexpected system header pragma"
PARENT_SCOPE)
endif()
endfunction()
check_system_header_pragma(empty ON)
if (RunCMake_TEST_FAILED)
return()
endif()
check_system_header_pragma(foo OFF)
@@ -0,0 +1,16 @@
set(CMAKE_INTERMEDIATE_DIR_STRATEGY FULL CACHE STRING "" FORCE)
enable_language(C)
add_library(empty empty.c)
target_precompile_headers(empty PUBLIC
<stdio.h>
<string.h>
)
add_library(foo foo.c)
target_precompile_headers(foo PUBLIC
<stdio.h>
<string.h>
)
set_target_properties(foo PROPERTIES PCH_WARNINGS ON)
@@ -24,6 +24,7 @@ if (RunCMake_GENERATOR MATCHES "(Ninja|Makefiles|Visual Studio)")
endif ()
run_test(PchInterfaceUnity)
run_cmake(PchPrologueEpilogue)
run_cmake(PchWarnings)
run_test(SkipPrecompileHeaders)
run_test(CXXnotC)
run_cmake(PchMultilanguage)
@@ -12,6 +12,7 @@ set(properties
"DISABLE_PRECOMPILE_HEADERS" "ON" "<SAME>"
"PCH_WARN_INVALID" "OFF" "<SAME>"
"PCH_INSTANTIATE_TEMPLATES" "OFF" "<SAME>"
"PCH_WARNINGS" "ON" "<SAME>"
## Platforms
### Android
"ANDROID_API" "9" "<SAME>"