From b8626261e95e0f99c9227d619671822f9a60ef1a Mon Sep 17 00:00:00 2001
From: Cristian Adam <cristian.adam@gmail.com>
Date: Sat, 13 Jul 2019 12:07:30 +0200
Subject: Precompile headers: Add methods to generate PCH sources

Co-Author: Daniel Pfeifer <daniel@pfeifer-mail.de>
---
 Modules/Compiler/Clang.cmake                   |   2 +
 Modules/Compiler/GNU.cmake                     |   8 ++
 Modules/Compiler/Intel.cmake                   |   7 ++
 Modules/Platform/Windows-Clang.cmake           |   7 ++
 Modules/Platform/Windows-Embarcadero.cmake     |   7 ++
 Modules/Platform/Windows-MSVC.cmake            |   9 ++
 Source/cmGeneratorTarget.cxx                   | 110 +++++++++++++++++++++++++
 Source/cmGeneratorTarget.h                     |   5 ++
 Source/cmLocalGenerator.cxx                    |  83 +++++++++++++++++++
 Source/cmLocalGenerator.h                      |   2 +
 Source/cmMakefileExecutableTargetGenerator.cxx |   2 +
 Source/cmMakefileLibraryTargetGenerator.cxx    |   2 +
 Source/cmMakefileTargetGenerator.cxx           |  20 ++++-
 Source/cmMakefileUtilityTargetGenerator.cxx    |   2 +
 Source/cmNinjaNormalTargetGenerator.cxx        |   2 +
 Source/cmNinjaTargetGenerator.cxx              |  16 +++-
 Utilities/IWYU/mapping.imp                     |   2 +
 17 files changed, 282 insertions(+), 4 deletions(-)

diff --git a/Modules/Compiler/Clang.cmake b/Modules/Compiler/Clang.cmake
index c3f13f3..45c33fb 100644
--- a/Modules/Compiler/Clang.cmake
+++ b/Modules/Compiler/Clang.cmake
@@ -96,5 +96,7 @@ else()
     set(CMAKE_${lang}_ARCHIVE_FINISH_IPO
       "\"${__ranlib}\" <TARGET>"
     )
+
+    set(CMAKE_PCH_PROLOGUE "#pragma clang system_header")
   endmacro()
 endif()
diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake
index 6b1bd3a..e0ff174 100644
--- a/Modules/Compiler/GNU.cmake
+++ b/Modules/Compiler/GNU.cmake
@@ -11,6 +11,9 @@ set(__COMPILER_GNU 1)
 include(Compiler/CMakeCommonCompilerMacros)
 include(Internal/CMakeCheckCompilerFlag)
 
+set(__pch_header_C "c-header")
+set(__pch_header_CXX "c++-header")
+
 macro(__compiler_gnu lang)
   # Feature flags.
   set(CMAKE_${lang}_VERBOSE_FLAG "-v")
@@ -104,4 +107,9 @@ macro(__compiler_gnu lang)
     unset(_COMPILER_ARGS)
   endif()
   list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "-dM" "-E" "-c" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp")
+
+  set(CMAKE_PCH_EXTENSION .gch)
+  set(CMAKE_PCH_PROLOGUE "#pragma GCC system_header")
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -include <PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -x ${__pch_header_${lang}} -include <PCH_HEADER>)
 endmacro()
diff --git a/Modules/Compiler/Intel.cmake b/Modules/Compiler/Intel.cmake
index f2f16e0..d895ed0 100644
--- a/Modules/Compiler/Intel.cmake
+++ b/Modules/Compiler/Intel.cmake
@@ -32,5 +32,12 @@ else()
       unset(_COMPILER_ARGS)
     endif()
     list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "-QdM" "-P" "-Za" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp")
+
+    # Precompile Headers
+    set(CMAKE_PCH_EXTENSION .pchi)
+    set(CMAKE_LINK_PCH ON)
+    set(CMAKE_PCH_EPILOGUE "#pragma hdrstop")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -Wno-pch-messages -pch-use <PCH_FILE> -include <PCH_HEADER>)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -Wno-pch-messages -pch-create <PCH_FILE> -include <PCH_HEADER>)
   endmacro()
 endif()
diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake
index 728e0b9..220649c 100644
--- a/Modules/Platform/Windows-Clang.cmake
+++ b/Modules/Platform/Windows-Clang.cmake
@@ -8,6 +8,9 @@ if(__WINDOWS_CLANG)
 endif()
 set(__WINDOWS_CLANG 1)
 
+set(__pch_header_C "c-header")
+set(__pch_header_CXX "c++-header")
+
 macro(__windows_compiler_clang_gnu lang)
   set(CMAKE_LIBRARY_PATH_FLAG "-L")
   set(CMAKE_LINK_LIBRARY_FLAG "-l")
@@ -73,6 +76,10 @@ macro(__windows_compiler_clang_gnu lang)
   string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG -Xclang -gcodeview ${__ADDED_FLAGS}")
   set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ")
 
+  set(CMAKE_PCH_EXTENSION .gch)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -include <PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -x ${__pch_header_${lang}} -include <PCH_HEADER>)
+
   unset(__ADDED_FLAGS)
   unset(__ADDED_FLAGS_DEBUG)
   string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_LOWER)
diff --git a/Modules/Platform/Windows-Embarcadero.cmake b/Modules/Platform/Windows-Embarcadero.cmake
index 48b936e..370b56e 100644
--- a/Modules/Platform/Windows-Embarcadero.cmake
+++ b/Modules/Platform/Windows-Embarcadero.cmake
@@ -119,6 +119,13 @@ macro(__embarcadero_language lang)
     "tlib ${CMAKE_START_TEMP_FILE}/p512 <LINK_FLAGS> /a <TARGET_QUOTED> <OBJECTS>${CMAKE_END_TEMP_FILE}"
     )
 
+  # Precompile Headers
+  if (EMBARCADERO)
+    set(CMAKE_PCH_EXTENSION .pch)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Xclang -include-pch -Xclang <PCH_FILE>)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Xclang -emit-pch -Xclang -include -Xclang <PCH_HEADER>)
+  endif()
+
   # Initial configuration flags.
   string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_tM}")
   string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -Od -v")
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake
index 7a83859..6ddcaa3 100644
--- a/Modules/Platform/Windows-MSVC.cmake
+++ b/Modules/Platform/Windows-MSVC.cmake
@@ -329,6 +329,15 @@ macro(__windows_compiler_msvc lang)
   set(CMAKE_${lang}_LINK_EXECUTABLE
     "${_CMAKE_VS_LINK_EXE}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES>${CMAKE_END_TEMP_FILE}")
 
+  set(CMAKE_PCH_EXTENSION .pch)
+  set(CMAKE_LINK_PCH ON)
+  if(MSVC_VERSION GREATER_EQUAL 1910)
+    # VS 2017 or greater
+    set(CMAKE_PCH_PROLOGUE "#pragma system_header")
+  endif()
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH /Yu<PCH_HEADER> /FI<PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH /Yc<PCH_HEADER> /FI<PCH_HEADER>)
+
   if("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xMSVC")
     set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
     set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 9e20b66..04738d8 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -21,6 +21,7 @@
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmCustomCommandLines.h"
+#include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionContext.h"
 #include "cmGeneratorExpressionDAGChecker.h"
@@ -3355,6 +3356,115 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
   return list;
 }
 
+std::string cmGeneratorTarget::GetPchHeader(const std::string& config,
+                                            const std::string& language) const
+{
+  if (language != "C" && language != "CXX") {
+    return std::string();
+  }
+  if (this->GetPropertyAsBool("DISABLE_PRECOMPILE_HEADERS")) {
+    return std::string();
+  }
+  const auto inserted =
+    this->PchHeaders.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    const std::vector<BT<std::string>> headers =
+      this->GetPrecompileHeaders(config, language);
+    if (headers.empty()) {
+      return std::string();
+    }
+    std::string& filename = inserted.first->second;
+
+    if (this->GetGlobalGenerator()->IsMultiConfig()) {
+      filename =
+        cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), "/");
+    } else {
+      // For GCC we need to have the header file .h[xx]
+      // next to the .h[xx].gch file
+      filename = this->ObjectDirectory;
+    }
+
+    filename = cmStrCat(filename, "CMakeFiles/", this->GetName(),
+                        ".dir/cmake_pch", ((language == "C") ? ".h" : ".hxx"));
+
+    const std::string filename_tmp = cmStrCat(filename, ".tmp");
+    {
+      auto pchPrologue = this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
+      auto pchEpilogue = this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE");
+
+      cmGeneratedFileStream file(
+        filename_tmp, false,
+        this->GetGlobalGenerator()->GetMakefileEncoding());
+      file << "/* generated by CMake */\n\n";
+      if (pchPrologue) {
+        file << pchPrologue << "\n";
+      }
+      if (this->GetGlobalGenerator()->IsXcode()) {
+        file << "#ifndef CMAKE_SKIP_PRECOMPILE_HEADERS\n";
+      }
+      if (language == "CXX") {
+        file << "#ifdef __cplusplus\n";
+      }
+      for (auto const& header_bt : headers) {
+        if (header_bt.Value.empty()) {
+          continue;
+        }
+        if (header_bt.Value[0] == '<' || header_bt.Value[0] == '"') {
+          file << "#include " << header_bt.Value << "\n";
+        } else {
+          file << "#include \"" << header_bt.Value << "\"\n";
+        }
+      }
+      if (language == "CXX") {
+        file << "#endif // __cplusplus\n";
+      }
+      if (this->GetGlobalGenerator()->IsXcode()) {
+        file << "#endif // CMAKE_SKIP_PRECOMPILE_HEADERS\n";
+      }
+      if (pchEpilogue) {
+        file << pchEpilogue << "\n";
+      }
+    }
+    cmSystemTools::CopyFileIfDifferent(filename_tmp, filename);
+    cmSystemTools::RemoveFile(filename_tmp);
+  }
+  return inserted.first->second;
+}
+
+std::string cmGeneratorTarget::GetPchSource(const std::string& config,
+                                            const std::string& language) const
+{
+  if (language != "C" && language != "CXX") {
+    return std::string();
+  }
+  const auto inserted =
+    this->PchSources.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    const std::string pchHeader = this->GetPchHeader(config, language);
+    if (pchHeader.empty()) {
+      return std::string();
+    }
+    std::string& filename = inserted.first->second;
+    filename = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
+                        "/CMakeFiles/", this->GetName(), ".dir/cmake_pch");
+
+    // For GCC the source extension will be tranformed into .h[xx].gch
+    if (!this->Makefile->IsOn("CMAKE_LINK_PCH")) {
+      filename += ((language == "C") ? ".h.c" : ".hxx.cxx");
+    } else {
+      filename += ((language == "C") ? ".c" : ".cxx");
+    }
+    const std::string filename_tmp = cmStrCat(filename, ".tmp");
+    {
+      cmGeneratedFileStream file(filename_tmp);
+      file << "/* generated by CMake */\n";
+    }
+    cmSystemTools::CopyFileIfDifferent(filename_tmp, filename);
+    cmSystemTools::RemoveFile(filename_tmp);
+  }
+  return inserted.first->second;
+}
+
 void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
                                        const std::string& config,
                                        const std::string& language) const
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 9d422ee..4701071 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -458,6 +458,11 @@ public:
   std::vector<BT<std::string>> GetPrecompileHeaders(
     const std::string& config, const std::string& language) const;
 
+  std::string GetPchHeader(const std::string& config,
+                           const std::string& language) const;
+  std::string GetPchSource(const std::string& config,
+                           const std::string& language) const;
+
   bool IsSystemIncludeDirectory(const std::string& dir,
                                 const std::string& config,
                                 const std::string& language) const;
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 57dabd1..d6d2f73 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -18,6 +18,7 @@
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
+#include "cmSourceFileLocationKind.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
@@ -37,6 +38,7 @@
 
 #include <algorithm>
 #include <assert.h>
+#include <functional>
 #include <initializer_list>
 #include <iterator>
 #include <memory>
@@ -2113,6 +2115,82 @@ void cmLocalGenerator::AppendFlagEscape(std::string& flags,
   this->AppendFlags(flags, this->EscapeForShell(rawFlag));
 }
 
+void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
+                                          const std::string& config)
+{
+  const std::string lang = target->GetLinkerLanguage(config);
+  const std::string buildType = cmSystemTools::UpperCase(config);
+  const std::string pchSource = target->GetPchSource(config, lang);
+  const std::string pchHeader = target->GetPchHeader(config, lang);
+
+  if (pchSource.empty() || pchHeader.empty()) {
+    return;
+  }
+
+  const std::string createOptVar =
+    cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_CREATE_PCH");
+  std::string createOptionList =
+    this->Makefile->GetSafeDefinition(createOptVar);
+
+  const std::string useOptVar =
+    cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_USE_PCH");
+  std::string useOptionList = this->Makefile->GetSafeDefinition(useOptVar);
+
+  const std::string pchExtension =
+    this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
+
+  if (createOptionList.empty() || useOptionList.empty() ||
+      pchExtension.empty()) {
+    return;
+  }
+
+  auto pch_sf = this->Makefile->GetOrCreateSource(
+    pchSource, false, cmSourceFileLocationKind::Known);
+  std::string pchFile = pchHeader;
+
+  if (!this->GetGlobalGenerator()->IsXcode()) {
+    // Exclude the pch files from linking
+    if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
+      cmSystemTools::ReplaceString(pchFile, (lang == "C" ? ".h" : ".hxx"),
+                                   pchExtension);
+      pch_sf->SetProperty("OBJECT_OUTPUTS", pchFile.c_str());
+    } else {
+      pchFile += pchExtension;
+      pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str());
+    }
+
+    target->AddSource(pchSource, true);
+
+    for (auto& str : { std::ref(useOptionList), std::ref(createOptionList) }) {
+      cmSystemTools::ReplaceString(str, "<PCH_HEADER>", pchHeader);
+      cmSystemTools::ReplaceString(str, "<PCH_FILE>", pchFile);
+    }
+  }
+
+  pch_sf->SetProperty("COMPILE_OPTIONS", createOptionList.c_str());
+
+  std::vector<cmSourceFile*> sources;
+  target->GetSourceFiles(sources, buildType);
+  for (cmSourceFile* sf : sources) {
+    if (pch_sf == sf || sf->GetLanguage() != lang) {
+      continue;
+    }
+
+    if (sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
+      if (this->GetGlobalGenerator()->IsXcode()) {
+        sf->SetProperty("COMPILE_DEFINITIONS",
+                        "CMAKE_SKIP_PRECOMPILE_HEADERS");
+      }
+      continue;
+    }
+
+    if (!this->GetGlobalGenerator()->IsXcode()) {
+      sf->SetProperty("OBJECT_DEPENDS", pchFile.c_str());
+      sf->SetProperty("COMPILE_OPTIONS", useOptionList.c_str());
+    }
+  }
+}
+
 void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
                                             cmGeneratorTarget* target,
                                             const std::string& config,
@@ -2705,6 +2783,11 @@ std::string cmLocalGenerator::GetObjectFileNameWithoutTarget(
       }
     }
 
+    const char* pchExtension = source.GetProperty("PCH_EXTENSION");
+    if (pchExtension) {
+      customOutputExtension = pchExtension;
+    }
+
     // Remove the source extension if it is to be replaced.
     if (replaceExt || customOutputExtension) {
       keptSourceExtension = false;
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index 455e491..f63fe0f 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -124,6 +124,8 @@ public:
   virtual void AppendFlags(std::string& flags, const char* newFlags) const;
   virtual void AppendFlagEscape(std::string& flags,
                                 const std::string& rawFlag) const;
+  void AddPchDependencies(cmGeneratorTarget* target,
+                          const std::string& config);
   void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target,
                             const std::string& config,
                             const std::string& lang);
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx
index 7bc05b1..bebd5c4 100644
--- a/Source/cmMakefileExecutableTargetGenerator.cxx
+++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -40,6 +40,8 @@ cmMakefileExecutableTargetGenerator::cmMakefileExecutableTargetGenerator(
   this->OSXBundleGenerator =
     cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName);
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
+
+  this->LocalGenerator->AddPchDependencies(target, this->ConfigName);
 }
 
 cmMakefileExecutableTargetGenerator::~cmMakefileExecutableTargetGenerator() =
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx
index 252279f..4244402 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -42,6 +42,8 @@ cmMakefileLibraryTargetGenerator::cmMakefileLibraryTargetGenerator(
   this->OSXBundleGenerator =
     cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName);
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
+
+  this->LocalGenerator->AddPchDependencies(target, this->ConfigName);
 }
 
 cmMakefileLibraryTargetGenerator::~cmMakefileLibraryTargetGenerator() =
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index f99fe4e..90d8ea9 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -239,10 +239,15 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
   this->GeneratorTarget->GetExtraSources(extraSources, config);
   this->OSXBundleGenerator->GenerateMacOSXContentStatements(
     extraSources, this->MacOSXContentGenerator);
+  const char* pchExtension =
+    this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
   std::vector<cmSourceFile const*> externalObjects;
   this->GeneratorTarget->GetExternalObjects(externalObjects, config);
   for (cmSourceFile const* sf : externalObjects) {
-    this->ExternalObjects.push_back(sf->GetFullPath());
+    auto const& objectFileName = sf->GetFullPath();
+    if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) {
+      this->ExternalObjects.push_back(objectFileName);
+    }
   }
   std::vector<cmSourceFile const*> objectSources;
   this->GeneratorTarget->GetObjectSources(objectSources, config);
@@ -1238,7 +1243,14 @@ void cmMakefileTargetGenerator::WriteObjectsVariable(
   if (!lineContinue) {
     lineContinue = "\\";
   }
+
+  const char* pchExtension =
+    this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
+
   for (std::string const& obj : this->Objects) {
+    if (cmSystemTools::StringEndsWith(obj, pchExtension)) {
+      continue;
+    }
     *this->BuildFileStream << " " << lineContinue << "\n";
     *this->BuildFileStream
       << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
@@ -1331,10 +1343,16 @@ private:
 void cmMakefileTargetGenerator::WriteObjectsStrings(
   std::vector<std::string>& objStrings, std::string::size_type limit)
 {
+  const char* pchExtension =
+    this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
+
   cmMakefileTargetGeneratorObjectStrings helper(
     objStrings, this->LocalGenerator,
     this->LocalGenerator->GetStateSnapshot().GetDirectory(), limit);
   for (std::string const& obj : this->Objects) {
+    if (cmSystemTools::StringEndsWith(obj, pchExtension)) {
+      continue;
+    }
     helper.Feed(obj);
   }
   for (std::string const& obj : this->ExternalObjects) {
diff --git a/Source/cmMakefileUtilityTargetGenerator.cxx b/Source/cmMakefileUtilityTargetGenerator.cxx
index 8ed6be5..556191f 100644
--- a/Source/cmMakefileUtilityTargetGenerator.cxx
+++ b/Source/cmMakefileUtilityTargetGenerator.cxx
@@ -25,6 +25,8 @@ cmMakefileUtilityTargetGenerator::cmMakefileUtilityTargetGenerator(
   this->OSXBundleGenerator =
     cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName);
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
+
+  this->LocalGenerator->AddPchDependencies(target, this->ConfigName);
 }
 
 cmMakefileUtilityTargetGenerator::~cmMakefileUtilityTargetGenerator() =
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 2841245..462746a 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -59,6 +59,8 @@ cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
   this->OSXBundleGenerator =
     cm::make_unique<cmOSXBundleGenerator>(target, this->GetConfigName());
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
+
+  GetLocalGenerator()->AddPchDependencies(target, this->GetConfigName());
 }
 
 cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default;
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 3d3d80d..29e8b74 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -790,10 +790,16 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements()
       extraSources, this->MacOSXContentGenerator.get());
   }
   {
+    const char* pchExtension =
+      GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
+
     std::vector<cmSourceFile const*> externalObjects;
     this->GeneratorTarget->GetExternalObjects(externalObjects, config);
     for (cmSourceFile const* sf : externalObjects) {
-      this->Objects.push_back(this->GetSourceFilePath(sf));
+      const auto objectFileName = this->GetSourceFilePath(sf);
+      if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) {
+        this->Objects.push_back(objectFileName);
+      }
     }
   }
 
@@ -955,8 +961,12 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
     vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"]);
 
   objBuild.Outputs.push_back(objectFileName);
-  // Add this object to the list of object files.
-  this->Objects.push_back(objectFileName);
+  const char* pchExtension =
+    this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
+  if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) {
+    // Add this object to the list of object files.
+    this->Objects.push_back(objectFileName);
+  }
 
   objBuild.ExplicitDeps.push_back(sourceFileName);
 
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index 9fff442..78026fa 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/Utilities/IWYU/mapping.imp
@@ -23,6 +23,7 @@
   # HACK: check whether this can be removed with next iwyu release.
   { include: [ "<bits/shared_ptr.h>", private, "<memory>", public ] },
   { include: [ "<bits/std_function.h>", private, "<functional>", public ] },
+  { include: [ "<bits/refwrap.h>", private, "<functional>", public ] },
   { include: [ "<bits/stdint-intn.h>", private, "<stdint.h>", public ] },
   { include: [ "<bits/stdint-uintn.h>", private, "<stdint.h>", public ] },
   { include: [ "<bits/time.h>", private, "<time.h>", public ] },
@@ -73,6 +74,7 @@
   # Use '-Xiwyu -v7' to see the fully qualified names that need this.
   # TODO: Can this be simplified with an @-expression?
   #{ symbol: [ "@std::__decay_and_strip<.*>::__type", private, "\"cmConfigure.h\"", public ] },
+  { symbol: [ "std::__decay_and_strip<char const (&)[1]>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmCommand *&>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmGeneratorTarget *&>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmFindCommon::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },
-- 
cgit v0.12