From 0467a2f91b632e9eb69805b7f0a2034ed4c8f39e Mon Sep 17 00:00:00 2001
From: Daniel Pfeifer <daniel@pfeifer-mail.de>
Date: Thu, 12 Mar 2015 22:44:38 +0100
Subject: PCH: add PRECOMPILE_HEADERS to special properties

---
 Source/cmExportBuildFileGenerator.cxx    |  3 +++
 Source/cmGeneratorExpressionDAGChecker.h |  3 ++-
 Source/cmGeneratorTarget.cxx             | 43 +++++++++++++++++++++++++++++
 Source/cmGeneratorTarget.h               |  7 +++++
 Source/cmTarget.cxx                      | 46 ++++++++++++++++++++++++++++++++
 Source/cmTarget.h                        |  5 ++++
 6 files changed, 106 insertions(+), 1 deletion(-)

diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index 8a1a305..9f0396b 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -91,6 +91,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);
+    this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte,
+                                    cmGeneratorExpression::BuildInterface,
+                                    properties, missingTargets);
     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);
diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h
index 6d7d6ef..b72a40f 100644
--- a/Source/cmGeneratorExpressionDAGChecker.h
+++ b/Source/cmGeneratorExpressionDAGChecker.h
@@ -29,7 +29,8 @@ class cmGeneratorTarget;
   SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES)                      \
   SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS)                              \
   SELECT(F, EvaluatingLinkDirectories, LINK_DIRECTORIES)                      \
-  SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)
+  SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)                              \
+  SELECT(F, EvaluatingPrecompileHeaders, PRECOMPILE_HEADERS)
 
 #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F)                                    \
   CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 3048c5f..9e20b66 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -278,6 +278,7 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
   , DebugCompileDefinitionsDone(false)
   , DebugLinkOptionsDone(false)
   , DebugLinkDirectoriesDone(false)
+  , DebugPrecompileHeadersDone(false)
   , DebugSourcesDone(false)
   , LinkImplementationLanguageIsContextDependent(true)
   , UtilityItemsDone(false)
@@ -312,6 +313,10 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
                                      t->GetLinkDirectoriesBacktraces(),
                                      this->LinkDirectoriesEntries);
 
+  CreatePropertyGeneratorExpressions(t->GetPrecompileHeadersEntries(),
+                                     t->GetPrecompileHeadersBacktraces(),
+                                     this->PrecompileHeadersEntries);
+
   CreatePropertyGeneratorExpressions(t->GetSourceEntries(),
                                      t->GetSourceBacktraces(),
                                      this->SourceEntries, true);
@@ -327,6 +332,7 @@ cmGeneratorTarget::~cmGeneratorTarget()
   cmDeleteAll(this->CompileDefinitionsEntries);
   cmDeleteAll(this->LinkOptionsEntries);
   cmDeleteAll(this->LinkDirectoriesEntries);
+  cmDeleteAll(this->PrecompileHeadersEntries);
   cmDeleteAll(this->SourceEntries);
   cmDeleteAll(this->LinkInformation);
 }
@@ -3312,6 +3318,43 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
   return list;
 }
 
+std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
+  const std::string& config, const std::string& language) const
+{
+  std::unordered_set<std::string> uniqueOptions;
+
+  cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS",
+                                             nullptr, nullptr);
+
+  std::vector<std::string> debugProperties;
+  const char* debugProp =
+    this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
+  if (debugProp) {
+    cmExpandList(debugProp, debugProperties);
+  }
+
+  bool debugDefines = !this->DebugPrecompileHeadersDone &&
+    std::find(debugProperties.begin(), debugProperties.end(),
+              "PRECOMPILE_HEADERS") != debugProperties.end();
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugPrecompileHeadersDone = true;
+  }
+
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, language, &dagChecker,
+                                  this->PrecompileHeadersEntries);
+
+  AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language,
+                      &dagChecker, entries);
+
+  std::vector<BT<std::string>> list;
+  processOptions(this, entries, list, uniqueOptions, debugDefines,
+                 "precompile headers", OptionsParse::None);
+
+  return list;
+}
+
 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 3874738..9d422ee 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -455,6 +455,9 @@ public:
   std::vector<BT<std::string>> GetLinkDepends(
     std::string const& config, std::string const& language) const;
 
+  std::vector<BT<std::string>> GetPrecompileHeaders(
+    const std::string& config, const std::string& language) const;
+
   bool IsSystemIncludeDirectory(const std::string& dir,
                                 const std::string& config,
                                 const std::string& language) const;
@@ -867,8 +870,11 @@ private:
   std::vector<TargetPropertyEntry*> CompileDefinitionsEntries;
   std::vector<TargetPropertyEntry*> LinkOptionsEntries;
   std::vector<TargetPropertyEntry*> LinkDirectoriesEntries;
+  std::vector<TargetPropertyEntry*> PrecompileHeadersEntries;
   std::vector<TargetPropertyEntry*> SourceEntries;
   mutable std::set<std::string> LinkImplicitNullProperties;
+  mutable std::map<std::string, std::string> PchHeaders;
+  mutable std::map<std::string, std::string> PchSources;
 
   void ExpandLinkItems(std::string const& prop, std::string const& value,
                        std::string const& config,
@@ -922,6 +928,7 @@ private:
   mutable bool DebugCompileDefinitionsDone;
   mutable bool DebugLinkOptionsDone;
   mutable bool DebugLinkDirectoriesDone;
+  mutable bool DebugPrecompileHeadersDone;
   mutable bool DebugSourcesDone;
   mutable bool LinkImplementationLanguageIsContextDependent;
   mutable bool UtilityItemsDone;
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index df37d32..d2bcf3f 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -190,6 +190,8 @@ public:
   std::vector<cmListFileBacktrace> CompileFeaturesBacktraces;
   std::vector<std::string> CompileDefinitionsEntries;
   std::vector<cmListFileBacktrace> CompileDefinitionsBacktraces;
+  std::vector<std::string> PrecompileHeadersEntries;
+  std::vector<cmListFileBacktrace> PrecompileHeadersBacktraces;
   std::vector<std::string> SourceEntries;
   std::vector<cmListFileBacktrace> SourceBacktraces;
   std::vector<std::string> LinkOptionsEntries;
@@ -348,6 +350,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     initProp("FOLDER");
     initProp("Swift_MODULE_DIRECTORY");
     initProp("VS_JUST_MY_CODE_DEBUGGING");
+    initProp("DISABLE_PRECOMPILE_HEADERS");
 #ifdef __APPLE__
     if (this->GetGlobalGenerator()->IsXcode()) {
       initProp("XCODE_GENERATE_SCHEME");
@@ -1017,6 +1020,16 @@ cmBacktraceRange cmTarget::GetCompileDefinitionsBacktraces() const
   return cmMakeRange(impl->CompileDefinitionsBacktraces);
 }
 
+cmStringRange cmTarget::GetPrecompileHeadersEntries() const
+{
+  return cmMakeRange(impl->PrecompileHeadersEntries);
+}
+
+cmBacktraceRange cmTarget::GetPrecompileHeadersBacktraces() const
+{
+  return cmMakeRange(impl->PrecompileHeadersBacktraces);
+}
+
 cmStringRange cmTarget::GetSourceEntries() const
 {
   return cmMakeRange(impl->SourceEntries);
@@ -1068,6 +1081,7 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
   MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
   MAKE_STATIC_PROP(COMPILE_FEATURES);
   MAKE_STATIC_PROP(COMPILE_OPTIONS);
+  MAKE_STATIC_PROP(PRECOMPILE_HEADERS);
   MAKE_STATIC_PROP(CUDA_PTX_COMPILATION);
   MAKE_STATIC_PROP(EXPORT_NAME);
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
@@ -1166,6 +1180,14 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
       cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
       impl->LinkDirectoriesBacktraces.push_back(lfbt);
     }
+  } else if (prop == propPRECOMPILE_HEADERS) {
+    impl->PrecompileHeadersEntries.clear();
+    impl->PrecompileHeadersBacktraces.clear();
+    if (value) {
+      impl->PrecompileHeadersEntries.emplace_back(value);
+      cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
+      impl->PrecompileHeadersBacktraces.push_back(lfbt);
+    }
   } else if (prop == propLINK_LIBRARIES) {
     impl->LinkImplementationPropertyEntries.clear();
     impl->LinkImplementationPropertyBacktraces.clear();
@@ -1282,6 +1304,12 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
       cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
       impl->LinkDirectoriesBacktraces.push_back(lfbt);
     }
+  } else if (prop == "PRECOMPILE_HEADERS") {
+    if (value && *value) {
+      impl->PrecompileHeadersEntries.emplace_back(value);
+      cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
+      impl->PrecompileHeadersBacktraces.push_back(lfbt);
+    }
   } else if (prop == "LINK_LIBRARIES") {
     if (value && *value) {
       cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
@@ -1393,6 +1421,13 @@ void cmTarget::InsertLinkDirectory(std::string const& entry,
   impl->LinkDirectoriesBacktraces.insert(btPosition, bt);
 }
 
+void cmTarget::InsertPrecompileHeader(std::string const& entry,
+                                      cmListFileBacktrace const& bt)
+{
+  impl->PrecompileHeadersEntries.push_back(entry);
+  impl->PrecompileHeadersBacktraces.push_back(bt);
+}
+
 static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
                                                   const char* value,
                                                   cmMakefile* context,
@@ -1513,6 +1548,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const
   MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
   MAKE_STATIC_PROP(LINK_OPTIONS);
   MAKE_STATIC_PROP(LINK_DIRECTORIES);
+  MAKE_STATIC_PROP(PRECOMPILE_HEADERS);
   MAKE_STATIC_PROP(IMPORTED);
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
   MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES);
@@ -1528,6 +1564,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const
     propCOMPILE_FEATURES,
     propCOMPILE_OPTIONS,
     propCOMPILE_DEFINITIONS,
+    propPRECOMPILE_HEADERS,
     propLINK_OPTIONS,
     propLINK_DIRECTORIES,
     propIMPORTED,
@@ -1616,6 +1653,15 @@ const char* cmTarget::GetProperty(const std::string& prop) const
       output = cmJoin(impl->Utilities, ";");
       return output.c_str();
     }
+    if (prop == propPRECOMPILE_HEADERS) {
+      if (impl->PrecompileHeadersEntries.empty()) {
+        return nullptr;
+      }
+
+      static std::string output;
+      output = cmJoin(impl->PrecompileHeadersEntries, ";");
+      return output.c_str();
+    }
     if (prop == propIMPORTED) {
       return this->IsImported() ? "TRUE" : "FALSE";
     }
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 2b75879..783c278 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -216,6 +216,8 @@ public:
                         cmListFileBacktrace const& bt, bool before = false);
   void InsertLinkDirectory(std::string const& entry,
                            cmListFileBacktrace const& bt, bool before = false);
+  void InsertPrecompileHeader(std::string const& entry,
+                              cmListFileBacktrace const& bt);
 
   void AppendBuildInterfaceIncludes();
 
@@ -237,6 +239,9 @@ public:
   cmStringRange GetCompileDefinitionsEntries() const;
   cmBacktraceRange GetCompileDefinitionsBacktraces() const;
 
+  cmStringRange GetPrecompileHeadersEntries() const;
+  cmBacktraceRange GetPrecompileHeadersBacktraces() const;
+
   cmStringRange GetSourceEntries() const;
   cmBacktraceRange GetSourceBacktraces() const;
 
-- 
cgit v0.12


From 9b6797e71d28d4ee201cb0f8ed5efa39784a2af3 Mon Sep 17 00:00:00 2001
From: Daniel Pfeifer <daniel@pfeifer-mail.de>
Date: Thu, 12 Mar 2015 17:44:36 +0100
Subject: PCH: add target_precompile_headers command

---
 Source/CMakeLists.txt                       |  2 ++
 Source/cmCommands.cxx                       |  4 +++
 Source/cmTargetPrecompileHeadersCommand.cxx | 36 +++++++++++++++++++++++++
 Source/cmTargetPrecompileHeadersCommand.h   | 41 +++++++++++++++++++++++++++++
 bootstrap                                   |  1 +
 5 files changed, 84 insertions(+)
 create mode 100644 Source/cmTargetPrecompileHeadersCommand.cxx
 create mode 100644 Source/cmTargetPrecompileHeadersCommand.h

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 7cd07a8..10dfeef 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -656,6 +656,8 @@ set(SRCS
   cmTargetLinkDirectoriesCommand.h
   cmTargetLinkLibrariesCommand.cxx
   cmTargetLinkLibrariesCommand.h
+  cmTargetPrecompileHeadersCommand.cxx
+  cmTargetPrecompileHeadersCommand.h
   cmTargetPropCommandBase.cxx
   cmTargetPropCommandBase.h
   cmTargetSourcesCommand.cxx
diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx
index ead16d2..41aabbb 100644
--- a/Source/cmCommands.cxx
+++ b/Source/cmCommands.cxx
@@ -78,6 +78,7 @@
 #include "cmTargetCompileOptionsCommand.h"
 #include "cmTargetIncludeDirectoriesCommand.h"
 #include "cmTargetLinkLibrariesCommand.h"
+#include "cmTargetPrecompileHeadersCommand.h"
 #include "cmTargetSourcesCommand.h"
 #include "cmTryCompileCommand.h"
 #include "cmTryRunCommand.h"
@@ -277,6 +278,9 @@ void GetProjectCommands(cmState* state)
   state->AddBuiltinCommand("try_compile",
                            cm::make_unique<cmTryCompileCommand>());
   state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>());
+  state->AddBuiltinCommand(
+    "target_precompile_headers",
+    cm::make_unique<cmTargetPrecompileHeadersCommand>());
 
 #if !defined(CMAKE_BOOTSTRAP)
   state->AddBuiltinCommand("add_compile_definitions",
diff --git a/Source/cmTargetPrecompileHeadersCommand.cxx b/Source/cmTargetPrecompileHeadersCommand.cxx
new file mode 100644
index 0000000..30cf1be
--- /dev/null
+++ b/Source/cmTargetPrecompileHeadersCommand.cxx
@@ -0,0 +1,36 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmTargetPrecompileHeadersCommand.h"
+
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
+#include "cmTarget.h"
+
+bool cmTargetPrecompileHeadersCommand::InitialPass(
+  std::vector<std::string> const& args, cmExecutionStatus&)
+{
+  return this->HandleArguments(args, "PRECOMPILE_HEADERS");
+}
+
+void cmTargetPrecompileHeadersCommand::HandleMissingTarget(
+  const std::string& name)
+{
+  const std::string e =
+    cmStrCat("Cannot specify precompile headers for target \"", name,
+             "\" which is not built by this project.");
+  this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+}
+
+std::string cmTargetPrecompileHeadersCommand::Join(
+  const std::vector<std::string>& content)
+{
+  return cmJoin(content, ";");
+}
+
+bool cmTargetPrecompileHeadersCommand::HandleDirectContent(
+  cmTarget* tgt, const std::vector<std::string>& content, bool, bool)
+{
+  tgt->AppendProperty("PRECOMPILE_HEADERS", this->Join(content).c_str());
+  return true;
+}
diff --git a/Source/cmTargetPrecompileHeadersCommand.h b/Source/cmTargetPrecompileHeadersCommand.h
new file mode 100644
index 0000000..1ddf2af
--- /dev/null
+++ b/Source/cmTargetPrecompileHeadersCommand.h
@@ -0,0 +1,41 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmTargetPrecompileHeadersCommand_h
+#define cmTargetPrecompileHeadersCommand_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+#include <vector>
+
+#include "cm_memory.hxx"
+
+#include "cmCommand.h"
+
+#include "cmTargetPropCommandBase.h"
+
+class cmExecutionStatus;
+class cmTarget;
+
+class cmTargetPrecompileHeadersCommand : public cmTargetPropCommandBase
+{
+public:
+  std::unique_ptr<cmCommand> Clone() override
+  {
+    return cm::make_unique<cmTargetPrecompileHeadersCommand>();
+  }
+
+  bool InitialPass(std::vector<std::string> const& args,
+                   cmExecutionStatus& status) override;
+
+private:
+  void HandleMissingTarget(const std::string& name) override;
+
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool prepend, bool system) override;
+
+  std::string Join(const std::vector<std::string>& content) override;
+};
+
+#endif
diff --git a/bootstrap b/bootstrap
index 42503e3..e5e4aef 100755
--- a/bootstrap
+++ b/bootstrap
@@ -436,6 +436,7 @@ CMAKE_CXX_SOURCES="\
   cmTargetCompileOptionsCommand \
   cmTargetIncludeDirectoriesCommand \
   cmTargetLinkLibrariesCommand \
+  cmTargetPrecompileHeadersCommand \
   cmTargetPropCommandBase \
   cmTargetPropertyComputer \
   cmTargetSourcesCommand \
-- 
cgit v0.12


From 375d01c6808713a0cfeef9ea092c8236ba063525 Mon Sep 17 00:00:00 2001
From: Daniel Pfeifer <daniel@pfeifer-mail.de>
Date: Thu, 12 Mar 2015 17:45:20 +0100
Subject: PCH: add example/test

---
 Tests/PrecompileHeaders/CMakeLists.txt | 17 +++++++++++++++++
 Tests/PrecompileHeaders/foo.c          |  6 ++++++
 Tests/PrecompileHeaders/foobar.c       |  7 +++++++
 Tests/PrecompileHeaders/include/bar.h  |  9 +++++++++
 Tests/PrecompileHeaders/include/foo.h  |  6 ++++++
 5 files changed, 45 insertions(+)
 create mode 100644 Tests/PrecompileHeaders/CMakeLists.txt
 create mode 100644 Tests/PrecompileHeaders/foo.c
 create mode 100644 Tests/PrecompileHeaders/foobar.c
 create mode 100644 Tests/PrecompileHeaders/include/bar.h
 create mode 100644 Tests/PrecompileHeaders/include/foo.h

diff --git a/Tests/PrecompileHeaders/CMakeLists.txt b/Tests/PrecompileHeaders/CMakeLists.txt
new file mode 100644
index 0000000..90e1915
--- /dev/null
+++ b/Tests/PrecompileHeaders/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.15)
+project(PrecompileHeaders C)
+
+add_library(foo foo.c)
+target_include_directories(foo PUBLIC include)
+target_precompile_headers(foo PUBLIC
+  foo.h
+  <stdio.h>
+  \"string.h\"
+)
+
+add_library(bar INTERFACE)
+target_include_directories(bar INTERFACE include)
+target_precompile_headers(bar INTERFACE bar.h)
+
+add_executable(foobar foobar.c)
+target_link_libraries(foobar foo bar)
diff --git a/Tests/PrecompileHeaders/foo.c b/Tests/PrecompileHeaders/foo.c
new file mode 100644
index 0000000..974a248
--- /dev/null
+++ b/Tests/PrecompileHeaders/foo.c
@@ -0,0 +1,6 @@
+#include "foo.h"
+
+int foo()
+{
+  return 0;
+}
diff --git a/Tests/PrecompileHeaders/foobar.c b/Tests/PrecompileHeaders/foobar.c
new file mode 100644
index 0000000..6dbf8ce
--- /dev/null
+++ b/Tests/PrecompileHeaders/foobar.c
@@ -0,0 +1,7 @@
+#include "bar.h"
+#include "foo.h"
+
+int main()
+{
+  return foo() + bar();
+}
diff --git a/Tests/PrecompileHeaders/include/bar.h b/Tests/PrecompileHeaders/include/bar.h
new file mode 100644
index 0000000..5feb983
--- /dev/null
+++ b/Tests/PrecompileHeaders/include/bar.h
@@ -0,0 +1,9 @@
+#ifndef bar_h
+#define bar_h
+
+static int bar()
+{
+  return 0;
+}
+
+#endif
diff --git a/Tests/PrecompileHeaders/include/foo.h b/Tests/PrecompileHeaders/include/foo.h
new file mode 100644
index 0000000..4a49474
--- /dev/null
+++ b/Tests/PrecompileHeaders/include/foo.h
@@ -0,0 +1,6 @@
+#ifndef foo_h
+#define foo_h
+
+extern int foo();
+
+#endif
-- 
cgit v0.12


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


From 28be170fbc97a29be6383fb4afe5f62c6983fb2c 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 support for Xcode generator

Co-Author: Daniel Pfeifer <daniel@pfeifer-mail.de>
---
 Source/cmGlobalXCodeGenerator.cxx | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index a204fe0..427ab44 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -2374,6 +2374,16 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
     buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION",
                                 this->CreateString(vso.str()));
   }
+
+  // Precompile Headers
+  std::string pchHeader = gtgt->GetPchHeader(configName, llang);
+  if (!pchHeader.empty()) {
+    buildSettings->AddAttribute("GCC_PREFIX_HEADER",
+                                this->CreateString(pchHeader));
+    buildSettings->AddAttribute("GCC_PRECOMPILE_PREFIX_HEADER",
+                                this->CreateString("YES"));
+  }
+
   // put this last so it can override existing settings
   // Convert "XCODE_ATTRIBUTE_*" properties directly.
   {
@@ -2829,6 +2839,8 @@ bool cmGlobalXCodeGenerator::CreateGroups(
         continue;
       }
 
+      generator->AddPchDependencies(gtgt, "");
+
       auto addSourceToGroup = [this, mf, gtgt,
                                &sourceGroups](std::string const& source) {
         cmSourceGroup* sourceGroup = mf->FindSourceGroup(source, sourceGroups);
-- 
cgit v0.12


From 519606704eac6df1314ec6691a1b77d2131a6dbc 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 support for Visual Studio generators

Tested with Visual C++ 2017 on Windows.

Co-Author: Daniel Pfeifer <daniel@pfeifer-mail.de>
---
 Source/cmGlobalVisualStudioGenerator.h     |  7 +++++++
 Source/cmLocalVisualStudio7Generator.cxx   |  2 ++
 Source/cmVisualStudio10TargetGenerator.cxx | 13 ++++++++++++-
 3 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index cbab329..e4cd73f 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -110,6 +110,13 @@ public:
 
   bool IsIncludeExternalMSProjectSupported() const override { return true; }
 
+  /** Get encoding used by generator for generated source files
+   */
+  codecvt::Encoding GetMakefileEncoding() const override
+  {
+    return codecvt::ANSI;
+  }
+
   class TargetSet : public std::set<cmGeneratorTarget const*>
   {
   };
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 2c91974..795cee4 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -1320,6 +1320,8 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout,
                                                     const std::string& libName,
                                                     cmGeneratorTarget* target)
 {
+  this->AddPchDependencies(target, "");
+
   std::vector<std::string> configs;
   this->Makefile->GetConfigurations(configs);
 
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 13f6295..749d234 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -249,6 +249,8 @@ cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator(
     this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
   this->InSourceBuild = (this->Makefile->GetCurrentSourceDirectory() ==
                          this->Makefile->GetCurrentBinaryDirectory());
+
+  this->LocalGenerator->AddPchDependencies(target, "");
 }
 
 cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator()
@@ -2145,6 +2147,9 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0)
       if (si.Kind == cmGeneratorTarget::SourceKindObjectSource) {
         this->OutputSourceSpecificFlags(e2, si.Source);
       }
+      if (si.Source->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
+        e2.Element("PrecompiledHeader", "NotUsing");
+      }
       if (!exclude_configs.empty()) {
         this->WriteExcludeFromBuild(e2, exclude_configs);
       }
@@ -2626,6 +2631,13 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
     this->IPOEnabledConfigurations.insert(configName);
   }
 
+  // Precompile Headers
+  std::string pchHeader =
+    this->GeneratorTarget->GetPchHeader(configName, linkLanguage);
+  if (this->MSTools && vcxproj == this->ProjectType && pchHeader.empty()) {
+    clOptions.AddFlag("PrecompiledHeader", "NotUsing");
+  }
+
   // Get preprocessor definitions for this directory.
   std::string defineFlags = this->Makefile->GetDefineFlags();
   if (this->MSTools) {
@@ -2641,7 +2653,6 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
         // replace this setting with "true" below.
         clOptions.AddFlag("UseFullPaths", "false");
       }
-      clOptions.AddFlag("PrecompiledHeader", "NotUsing");
       clOptions.AddFlag("AssemblerListingLocation", "$(IntDir)");
     }
   }
-- 
cgit v0.12


From 577293016430115a2d7dec5e3588f12bcabc96ec Mon Sep 17 00:00:00 2001
From: Cristian Adam <cristian.adam@gmail.com>
Date: Wed, 31 Jul 2019 16:08:49 +0200
Subject: Precompile headers: Add unit tests

---
 Tests/PrecompileHeaders/CMakeLists.txt             | 17 ------
 Tests/PrecompileHeaders/foo.c                      |  6 --
 Tests/PrecompileHeaders/foobar.c                   |  7 ---
 Tests/PrecompileHeaders/include/bar.h              |  9 ---
 Tests/PrecompileHeaders/include/foo.h              |  6 --
 Tests/RunCMake/CMakeLists.txt                      |  2 +
 Tests/RunCMake/PrecompileHeaders/CMakeLists.txt    |  3 +
 .../PrecompileHeaders/DisabledPch-check.cmake      | 17 ++++++
 Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake | 14 +++++
 .../PrecompileHeaders/PchInterface-check.cmake     | 36 +++++++++++
 .../RunCMake/PrecompileHeaders/PchInterface.cmake  | 20 +++++++
 .../PchPrologueEpilogue-check.cmake                | 12 ++++
 .../PrecompileHeaders/PchPrologueEpilogue.cmake    | 11 ++++
 .../RunCMake/PrecompileHeaders/RunCMakeTest.cmake  | 18 ++++++
 .../PrecompileHeaders/SkipPrecompileHeaders.cmake  | 13 ++++
 Tests/RunCMake/PrecompileHeaders/foo.c             |  6 ++
 Tests/RunCMake/PrecompileHeaders/foobar.c          |  7 +++
 Tests/RunCMake/PrecompileHeaders/include/bar.h     |  9 +++
 Tests/RunCMake/PrecompileHeaders/include/foo.h     |  6 ++
 Tests/RunCMake/PrecompileHeaders/main.cpp          |  4 ++
 Tests/RunCMake/PrecompileHeaders/non-pch.cpp       |  3 +
 Tests/RunCMake/PrecompileHeaders/pch.h             |  3 +
 Tests/RunCMake/VS10Project/RunCMakeTest.cmake      |  1 +
 .../VS10Project/VsPrecompileHeaders-check.cmake    | 69 ++++++++++++++++++++++
 .../RunCMake/VS10Project/VsPrecompileHeaders.cmake |  4 ++
 Tests/RunCMake/XcodeProject/RunCMakeTest.cmake     |  1 +
 .../XcodePrecompileHeaders-check.cmake             | 35 +++++++++++
 .../XcodeProject/XcodePrecompileHeaders.cmake      |  4 ++
 28 files changed, 298 insertions(+), 45 deletions(-)
 delete mode 100644 Tests/PrecompileHeaders/CMakeLists.txt
 delete mode 100644 Tests/PrecompileHeaders/foo.c
 delete mode 100644 Tests/PrecompileHeaders/foobar.c
 delete mode 100644 Tests/PrecompileHeaders/include/bar.h
 delete mode 100644 Tests/PrecompileHeaders/include/foo.h
 create mode 100644 Tests/RunCMake/PrecompileHeaders/CMakeLists.txt
 create mode 100644 Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake
 create mode 100644 Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake
 create mode 100644 Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake
 create mode 100644 Tests/RunCMake/PrecompileHeaders/PchInterface.cmake
 create mode 100644 Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake
 create mode 100644 Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake
 create mode 100644 Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
 create mode 100644 Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake
 create mode 100644 Tests/RunCMake/PrecompileHeaders/foo.c
 create mode 100644 Tests/RunCMake/PrecompileHeaders/foobar.c
 create mode 100644 Tests/RunCMake/PrecompileHeaders/include/bar.h
 create mode 100644 Tests/RunCMake/PrecompileHeaders/include/foo.h
 create mode 100644 Tests/RunCMake/PrecompileHeaders/main.cpp
 create mode 100644 Tests/RunCMake/PrecompileHeaders/non-pch.cpp
 create mode 100644 Tests/RunCMake/PrecompileHeaders/pch.h
 create mode 100644 Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake
 create mode 100644 Tests/RunCMake/VS10Project/VsPrecompileHeaders.cmake
 create mode 100644 Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake
 create mode 100644 Tests/RunCMake/XcodeProject/XcodePrecompileHeaders.cmake

diff --git a/Tests/PrecompileHeaders/CMakeLists.txt b/Tests/PrecompileHeaders/CMakeLists.txt
deleted file mode 100644
index 90e1915..0000000
--- a/Tests/PrecompileHeaders/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 3.15)
-project(PrecompileHeaders C)
-
-add_library(foo foo.c)
-target_include_directories(foo PUBLIC include)
-target_precompile_headers(foo PUBLIC
-  foo.h
-  <stdio.h>
-  \"string.h\"
-)
-
-add_library(bar INTERFACE)
-target_include_directories(bar INTERFACE include)
-target_precompile_headers(bar INTERFACE bar.h)
-
-add_executable(foobar foobar.c)
-target_link_libraries(foobar foo bar)
diff --git a/Tests/PrecompileHeaders/foo.c b/Tests/PrecompileHeaders/foo.c
deleted file mode 100644
index 974a248..0000000
--- a/Tests/PrecompileHeaders/foo.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "foo.h"
-
-int foo()
-{
-  return 0;
-}
diff --git a/Tests/PrecompileHeaders/foobar.c b/Tests/PrecompileHeaders/foobar.c
deleted file mode 100644
index 6dbf8ce..0000000
--- a/Tests/PrecompileHeaders/foobar.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "bar.h"
-#include "foo.h"
-
-int main()
-{
-  return foo() + bar();
-}
diff --git a/Tests/PrecompileHeaders/include/bar.h b/Tests/PrecompileHeaders/include/bar.h
deleted file mode 100644
index 5feb983..0000000
--- a/Tests/PrecompileHeaders/include/bar.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef bar_h
-#define bar_h
-
-static int bar()
-{
-  return 0;
-}
-
-#endif
diff --git a/Tests/PrecompileHeaders/include/foo.h b/Tests/PrecompileHeaders/include/foo.h
deleted file mode 100644
index 4a49474..0000000
--- a/Tests/PrecompileHeaders/include/foo.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef foo_h
-#define foo_h
-
-extern int foo();
-
-#endif
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 0e93cf8..9a31916 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -573,3 +573,5 @@ if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|9[0-9])")
 endif()
 
 add_RunCMake_test("CTestCommandExpandLists")
+
+add_RunCMake_test(PrecompileHeaders)
diff --git a/Tests/RunCMake/PrecompileHeaders/CMakeLists.txt b/Tests/RunCMake/PrecompileHeaders/CMakeLists.txt
new file mode 100644
index 0000000..7dbf32e
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.15.0)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake b/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake
new file mode 100644
index 0000000..fa37c2c
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake
@@ -0,0 +1,17 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/CMakeFiles/foo.dir/cmake_pch.h")
+    set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/CMakeFiles/foobar.dir/cmake_pch.h")
+else()
+    set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/cmake_pch.h")
+    set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h")
+endif()
+
+if (NOT EXISTS ${foo_pch_header})
+   set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} does not exist")
+   return()
+endif()
+
+if (EXISTS ${foobar_pch_header})
+   set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} should not exist")
+   return()
+endif()
diff --git a/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake b/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake
new file mode 100644
index 0000000..ee47980
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.15)
+project(DisabledPch C)
+
+add_library(foo foo.c)
+target_include_directories(foo PUBLIC include)
+target_precompile_headers(foo PUBLIC
+  foo.h
+  <stdio.h>
+  \"string.h\"
+)
+
+add_executable(foobar foobar.c)
+target_link_libraries(foobar foo)
+set_target_properties(foobar PROPERTIES DISABLE_PRECOMPILE_HEADERS ON)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake
new file mode 100644
index 0000000..cbd6ede
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake
@@ -0,0 +1,36 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/CMakeFiles/foo.dir/cmake_pch.h")
+  set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/CMakeFiles/foobar.dir/cmake_pch.h")
+else()
+  set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/cmake_pch.h")
+  set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h")
+endif()
+
+if (NOT EXISTS ${foo_pch_header})
+  set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} does not exist")
+  return()
+endif()
+
+if (NOT EXISTS ${foobar_pch_header})
+  set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} does not exist")
+  return()
+endif()
+
+file(STRINGS ${foo_pch_header} foo_pch_header_strings)
+
+if (NOT "#include \"foo.h\"" IN_LIST foo_pch_header_strings OR
+    NOT "#include <stdio.h>" IN_LIST foo_pch_header_strings OR
+    NOT "#include \"string.h\"" IN_LIST foo_pch_header_strings)
+  set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} has bad content")
+  return()
+endif()
+
+file(STRINGS ${foobar_pch_header} foobar_pch_header_strings)
+
+if (NOT "#include \"foo.h\"" IN_LIST foobar_pch_header_strings OR
+    NOT "#include <stdio.h>" IN_LIST foobar_pch_header_strings OR
+    NOT "#include \"string.h\"" IN_LIST foobar_pch_header_strings OR
+    NOT "#include \"bar.h\"" IN_LIST foobar_pch_header_strings)
+  set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} has bad content")
+  return()
+endif()
diff --git a/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake b/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake
new file mode 100644
index 0000000..9041b09
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchInterface.cmake
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.15)
+project(PchInterface C)
+
+add_library(foo foo.c)
+target_include_directories(foo PUBLIC include)
+target_precompile_headers(foo PUBLIC
+  foo.h
+  <stdio.h>
+  \"string.h\"
+)
+
+add_library(bar INTERFACE)
+target_include_directories(bar INTERFACE include)
+target_precompile_headers(bar INTERFACE bar.h)
+
+add_executable(foobar foobar.c)
+target_link_libraries(foobar foo bar)
+
+enable_testing()
+add_test(NAME foobar COMMAND foobar)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake
new file mode 100644
index 0000000..fd82607
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake
@@ -0,0 +1,12 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(main_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/main.dir/CMakeFiles/main.dir/cmake_pch.hxx")
+else()
+    set(main_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/main.dir/cmake_pch.hxx")
+endif()
+
+file(STRINGS ${main_pch_header} main_pch_header_strings)
+string(REGEX MATCH "#pragma warning\\(push, 0\\).*#include.*pch.h.*#pragma warning\\(pop\\)" matched_code ${main_pch_header_strings})
+if(NOT matched_code)
+  set(RunCMake_TEST_FAILED "Generated pch file doesn't include expected prologue and epilogue code")
+  return()
+endif()
diff --git a/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake
new file mode 100644
index 0000000..3e27620
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(PchPrologueEpilogue)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_PCH_PROLOGUE "#pragma warning(push, 0)")
+set(CMAKE_PCH_EPILOGUE "#pragma warning(pop)")
+
+add_executable(main main.cpp)
+target_precompile_headers(main PRIVATE pch.h)
diff --git a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
new file mode 100644
index 0000000..fffcc47
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
@@ -0,0 +1,18 @@
+cmake_policy(SET CMP0057 NEW)
+include(RunCMake)
+
+function(run_test name)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
+  run_cmake(${name})
+  # Precompiled headers are not supported with multiple architectures.
+  if(NOT "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]")
+    set(RunCMake_TEST_NO_CLEAN 1)
+    run_cmake_command(${name}-build ${CMAKE_COMMAND} --build . --config Debug)
+    run_cmake_command(${name}-test ${CMAKE_CTEST_COMMAND} -C Debug)
+  endif()
+endfunction()
+
+run_cmake(DisabledPch)
+run_test(PchInterface)
+run_cmake(PchPrologueEpilogue)
+run_test(SkipPrecompileHeaders)
diff --git a/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake b/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake
new file mode 100644
index 0000000..49efbfb
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(SkipPrecompileHeaders)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_executable(pch-test main.cpp non-pch.cpp)
+target_precompile_headers(pch-test PRIVATE pch.h)
+
+set_source_files_properties(non-pch.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+
+enable_testing()
+add_test(NAME pch-test COMMAND pch-test)
diff --git a/Tests/RunCMake/PrecompileHeaders/foo.c b/Tests/RunCMake/PrecompileHeaders/foo.c
new file mode 100644
index 0000000..974a248
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/foo.c
@@ -0,0 +1,6 @@
+#include "foo.h"
+
+int foo()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/PrecompileHeaders/foobar.c b/Tests/RunCMake/PrecompileHeaders/foobar.c
new file mode 100644
index 0000000..6dbf8ce
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/foobar.c
@@ -0,0 +1,7 @@
+#include "bar.h"
+#include "foo.h"
+
+int main()
+{
+  return foo() + bar();
+}
diff --git a/Tests/RunCMake/PrecompileHeaders/include/bar.h b/Tests/RunCMake/PrecompileHeaders/include/bar.h
new file mode 100644
index 0000000..5feb983
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/include/bar.h
@@ -0,0 +1,9 @@
+#ifndef bar_h
+#define bar_h
+
+static int bar()
+{
+  return 0;
+}
+
+#endif
diff --git a/Tests/RunCMake/PrecompileHeaders/include/foo.h b/Tests/RunCMake/PrecompileHeaders/include/foo.h
new file mode 100644
index 0000000..4a49474
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/include/foo.h
@@ -0,0 +1,6 @@
+#ifndef foo_h
+#define foo_h
+
+extern int foo();
+
+#endif
diff --git a/Tests/RunCMake/PrecompileHeaders/main.cpp b/Tests/RunCMake/PrecompileHeaders/main.cpp
new file mode 100644
index 0000000..f8b643a
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/main.cpp
@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/PrecompileHeaders/non-pch.cpp b/Tests/RunCMake/PrecompileHeaders/non-pch.cpp
new file mode 100644
index 0000000..df5a79f
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/non-pch.cpp
@@ -0,0 +1,3 @@
+#ifdef PCH_INCLUDED
+#  error "PCH must not be included into this file!"
+#endif
diff --git a/Tests/RunCMake/PrecompileHeaders/pch.h b/Tests/RunCMake/PrecompileHeaders/pch.h
new file mode 100644
index 0000000..81b6d9e
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/pch.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define PCH_INCLUDED 1
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index 5ca069a..72154e7 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -25,6 +25,7 @@ run_cmake(VsProjectImport)
 run_cmake(VsPackageReferences)
 run_cmake(VsDpiAware)
 run_cmake(VsDpiAwareBadParam)
+run_cmake(VsPrecompileHeaders)
 
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.05)
   run_cmake(VsJustMyCode)
diff --git a/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake b/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake
new file mode 100644
index 0000000..82ca421
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake
@@ -0,0 +1,69 @@
+set(pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/cmake_pch.hxx")
+set(pch_source "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/cmake_pch.cxx")
+
+file(TO_NATIVE_PATH "${pch_source}" pch_source_win)
+
+if(NOT EXISTS "${pch_header}")
+  set(RunCMake_TEST_FAILED "Generated PCH header ${pch_header} does not exist.")
+  return()
+endif()
+if(NOT EXISTS "${pch_source}")
+  set(RunCMake_TEST_FAILED "Generated PCH header ${pch_source} does not exist.")
+  return()
+endif()
+
+set(tgt_project "${RunCMake_TEST_BINARY_DIR}/tgt.vcxproj")
+if (NOT EXISTS "${tgt_project}")
+  set(RunCMake_TEST_FAILED "Generated project file ${tgt_project} doesn't exist.")
+  return()
+endif()
+
+file(STRINGS ${tgt_project} tgt_projects_strings)
+
+foreach(line IN LISTS tgt_projects_strings)
+  if (line MATCHES "<PrecompiledHeader.*>Use</PrecompiledHeader>")
+    set(have_pch_use ON)
+  endif()
+
+  if (line MATCHES "<PrecompiledHeader.*>Create</PrecompiledHeader>")
+    set(have_pch_create ON)
+  endif()
+
+  if (line MATCHES "<PrecompiledHeaderFile.*>${pch_header}</PrecompiledHeaderFile>")
+    set(have_pch_header ON)
+  endif()
+
+  if (line MATCHES "<ForcedIncludeFiles.*>${pch_header}</ForcedIncludeFiles>")
+    set(have_force_pch_header ON)
+  endif()
+
+  string(FIND "${line}" "<ClCompile Include=\"${pch_source_win}\">" find_pos)
+  if (NOT find_pos EQUAL "-1")
+    set(have_pch_source_compile ON)
+  endif()
+endforeach()
+
+if (NOT have_pch_use)
+  set(RunCMake_TEST_FAILED "Generated project should have the <PrecompiledHeader>Use</PrecompiledHeader> block.")
+  return()
+endif()
+
+if (NOT have_pch_create)
+  set(RunCMake_TEST_FAILED "Generated project should have the <PrecompiledHeader>Create</PrecompiledHeader> block.")
+  return()
+endif()
+
+if (NOT have_pch_header)
+  set(RunCMake_TEST_FAILED "Generated project should have the <PrecompiledHeaderFile>${pch_header}</PrecompiledHeaderFile> block.")
+  return()
+endif()
+
+if (NOT have_force_pch_header)
+  set(RunCMake_TEST_FAILED "Generated project should have the <ForcedIncludeFiles>${pch_header}</ForcedIncludeFiles> block.")
+  return()
+endif()
+
+if (NOT have_pch_source_compile)
+  set(RunCMake_TEST_FAILED "Generated project should have the <ClCompile Include=\"${pch_source_win}\"> block.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsPrecompileHeaders.cmake b/Tests/RunCMake/VS10Project/VsPrecompileHeaders.cmake
new file mode 100644
index 0000000..6d208c9
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsPrecompileHeaders.cmake
@@ -0,0 +1,4 @@
+project(VsPrecompileHeaders CXX)
+
+add_library(tgt SHARED empty.cxx)
+target_precompile_headers(tgt PRIVATE stdafx.h)
diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
index 191f56d..1dfa8b2 100644
--- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
@@ -12,6 +12,7 @@ run_cmake(XcodeObjectNeedsQuote)
 run_cmake(XcodeOptimizationFlags)
 run_cmake(XcodePreserveNonOptimizationFlags)
 run_cmake(XcodePreserveObjcFlag)
+run_cmake(XcodePrecompileHeaders)
 if (NOT XCODE_VERSION VERSION_LESS 6)
   run_cmake(XcodePlatformFrameworks)
 endif()
diff --git a/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake
new file mode 100644
index 0000000..aa3eafc
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake
@@ -0,0 +1,35 @@
+set(pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/cmake_pch.hxx")
+
+if(NOT EXISTS "${pch_header}")
+  set(RunCMake_TEST_FAILED "Generated PCH header ${pch_header} does not exist.")
+  return()
+endif()
+
+set(tgt_project "${RunCMake_TEST_BINARY_DIR}/XcodePrecompileHeaders.xcodeproj/project.pbxproj")
+if (NOT EXISTS "${tgt_project}")
+  set(RunCMake_TEST_FAILED "Generated project file ${tgt_project} doesn't exist.")
+  return()
+endif()
+
+file(STRINGS ${tgt_project} tgt_projects_strings)
+
+foreach(line IN LISTS tgt_projects_strings)
+  if (line MATCHES "GCC_PRECOMPILE_PREFIX_HEADER = YES;")
+    set(have_pch_prefix ON)
+  endif()
+
+  string(FIND "${line}" "GCC_PREFIX_HEADER = \"${pch_header}\";" find_pos)
+  if (NOT find_pos EQUAL "-1")
+    set(have_pch_header ON)
+  endif()
+endforeach()
+
+if (NOT have_pch_prefix)
+  set(RunCMake_TEST_FAILED "Generated project should have the GCC_PRECOMPILE_PREFIX_HEADER = YES; line.")
+  return()
+endif()
+
+if (NOT have_pch_header)
+  set(RunCMake_TEST_FAILED "Generated project should have the GCC_PREFIX_HEADER = \"${pch_header}\"; line.")
+  return()
+endif()
diff --git a/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders.cmake b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders.cmake
new file mode 100644
index 0000000..f86bcf4
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders.cmake
@@ -0,0 +1,4 @@
+project(XcodePrecompileHeaders CXX)
+
+add_library(tgt foo.cpp)
+target_precompile_headers(tgt PRIVATE stdafx.h)
-- 
cgit v0.12


From 8da78d4efe0e4d0ef8708ecd94cf886c3f7d9ae4 Mon Sep 17 00:00:00 2001
From: Cristian Adam <cristian.adam@gmail.com>
Date: Wed, 31 Jul 2019 18:54:44 +0200
Subject: Precompile headers: Update documentation

---
 Help/command/target_precompile_headers.rst         | 56 ++++++++++++++++++++++
 Help/manual/cmake-commands.7.rst                   |  1 +
 Help/manual/cmake-properties.7.rst                 |  4 ++
 Help/manual/cmake-variables.7.rst                  |  1 +
 Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst           | 13 +++++
 Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst       |  8 ++++
 Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst     | 14 ++++++
 Help/prop_tgt/PRECOMPILE_HEADERS.rst               | 12 +++++
 Help/release/dev/precompile-headers.rst            |  6 +++
 Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst |  6 +++
 10 files changed, 121 insertions(+)
 create mode 100644 Help/command/target_precompile_headers.rst
 create mode 100644 Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst
 create mode 100644 Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst
 create mode 100644 Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst
 create mode 100644 Help/prop_tgt/PRECOMPILE_HEADERS.rst
 create mode 100644 Help/release/dev/precompile-headers.rst
 create mode 100644 Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst

diff --git a/Help/command/target_precompile_headers.rst b/Help/command/target_precompile_headers.rst
new file mode 100644
index 0000000..3e28265
--- /dev/null
+++ b/Help/command/target_precompile_headers.rst
@@ -0,0 +1,56 @@
+target_precompile_headers
+-------------------------
+
+Add a list of header files to precompile.
+
+.. code-block:: cmake
+
+  target_precompile_headers(<target>
+    <INTERFACE|PUBLIC|PRIVATE> [header1...]
+    [<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
+
+Adds header files to :prop_tgt:`PRECOMPILE_HEADERS` or
+:prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` target properties.
+
+Precompiling header files can speed up compilation by creating a partially
+processed version of some header files, and then using that version during
+compilations rather than repeatedly parsing the original headers.
+
+The named ``<target>`` must have been created by a command such as
+:command:`add_executable` or :command:`add_library` and must not be an
+:ref:`ALIAS target <Alias Targets>`.
+
+The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
+specify the scope of the following arguments.  ``PRIVATE`` and ``PUBLIC``
+items will populate the :prop_tgt:`PRECOMPILE_HEADERS` property of
+``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
+:prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` property of ``<target>``.
+(:ref:`IMPORTED targets <Imported Targets>` only support ``INTERFACE`` items.)
+Repeated calls for the same ``<target>`` append items in the order called.
+
+Arguments to ``target_precompile_headers`` may use "generator expressions"
+with the syntax ``$<...>``.
+See the :manual:`cmake-generator-expressions(7)` manual for available
+expressions.  See the :manual:`cmake-compile-features(7)` manual for
+information on compile features and a list of supported compilers.
+
+.. code-block:: cmake
+
+  target_precompile_headers(<target>
+    PUBLIC
+      "project_header.h"
+    PRIVATE
+      <unordered_map>
+  )
+
+Header files will be double quoted if they are not specified with double
+quotes or angle brackets.
+
+See Also
+^^^^^^^^
+
+For disabling precompile headers for specific targets there is the
+property :prop_tgt:`DISABLE_PRECOMPILE_HEADERS`.
+
+For skipping certain source files there is the source file property
+:prop_sf:`SKIP_PRECOMPILE_HEADERS`.
diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst
index a3bc465..59ba897 100644
--- a/Help/manual/cmake-commands.7.rst
+++ b/Help/manual/cmake-commands.7.rst
@@ -112,6 +112,7 @@ These commands are available only in CMake projects.
    /command/target_link_directories
    /command/target_link_libraries
    /command/target_link_options
+   /command/target_precompile_headers
    /command/target_sources
    /command/try_compile
    /command/try_run
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 62d23c7..03f18fc 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -181,6 +181,7 @@ Properties on Targets
    /prop_tgt/DEFINE_SYMBOL
    /prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY
    /prop_tgt/DEPLOYMENT_ADDITIONAL_FILES
+   /prop_tgt/DISABLE_PRECOMPILE_HEADERS
    /prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION
    /prop_tgt/EchoString
    /prop_tgt/ENABLE_EXPORTS
@@ -240,6 +241,7 @@ Properties on Targets
    /prop_tgt/INTERFACE_LINK_DIRECTORIES
    /prop_tgt/INTERFACE_LINK_LIBRARIES
    /prop_tgt/INTERFACE_LINK_OPTIONS
+   /prop_tgt/INTERFACE_PRECOMPILE_HEADERS
    /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
    /prop_tgt/INTERFACE_SOURCES
    /prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
@@ -295,6 +297,7 @@ Properties on Targets
    /prop_tgt/PDB_OUTPUT_DIRECTORY_CONFIG
    /prop_tgt/PDB_OUTPUT_DIRECTORY
    /prop_tgt/POSITION_INDEPENDENT_CODE
+   /prop_tgt/PRECOMPILE_HEADERS
    /prop_tgt/PREFIX
    /prop_tgt/PRIVATE_HEADER
    /prop_tgt/PROJECT_LABEL
@@ -445,6 +448,7 @@ Properties on Source Files
    /prop_sf/SKIP_AUTOMOC
    /prop_sf/SKIP_AUTORCC
    /prop_sf/SKIP_AUTOUIC
+   /prop_sf/SKIP_PRECOMPILE_HEADERS
    /prop_sf/Swift_DEPENDENCIES_FILE
    /prop_sf/Swift_DIAGNOSTICS_FILE
    /prop_sf/SYMBOLIC
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 9ad1195..668b2a2 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -355,6 +355,7 @@ Variables that Control the Build
    /variable/CMAKE_CUDA_SEPARABLE_COMPILATION
    /variable/CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS
    /variable/CMAKE_DEBUG_POSTFIX
+   /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS
    /variable/CMAKE_ENABLE_EXPORTS
    /variable/CMAKE_EXE_LINKER_FLAGS
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG
diff --git a/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst b/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..5f39f30
--- /dev/null
+++ b/Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,13 @@
+SKIP_PRECOMPILE_HEADERS
+-----------------------
+
+Is this source file skipped by :prop_tgt:`PRECOMPILE_HEADERS` feature.
+
+This property helps with build problems that one would run into
+when using the :prop_tgt:`PRECOMPILE_HEADERS` feature.
+
+One example would be the usage of Objective-C (*.m) files, and
+Objective-C++ (*.mm) files, which lead to compilation failure
+because they are treated (in case of Ninja / Makefile generator)
+as C, and CXX respectively. The precompile headers are not
+compatible between languages.
diff --git a/Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst b/Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..4cef023
--- /dev/null
+++ b/Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,8 @@
+DISABLE_PRECOMPILE_HEADERS
+--------------------------
+
+Disables the precompilation of header files specified by
+:prop_tgt:`PRECOMPILE_HEADERS` property.
+
+If the property is not set, CMake will use the value provided
+by :variable:`CMAKE_DISABLE_PRECOMPILE_HEADERS`.
diff --git a/Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst b/Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..8ff7e8b
--- /dev/null
+++ b/Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,14 @@
+INTERFACE_PRECOMPILE_HEADERS
+----------------------------
+
+List of interface header files to precompile into consuming targets.
+
+Targets may populate this property to publish the header files
+for consuming targets to precompile.  The :command:`target_precompile_headers`
+command populates this property with values given to the ``PUBLIC`` and
+``INTERFACE`` keywords.  Projects may also get and set the property directly.
+
+Contents of ``INTERFACE_PRECOMPILE_HEADERS`` may use "generator expressions"
+with the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
+manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
+manual for more on defining buildsystem properties.
diff --git a/Help/prop_tgt/PRECOMPILE_HEADERS.rst b/Help/prop_tgt/PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..9e70b65
--- /dev/null
+++ b/Help/prop_tgt/PRECOMPILE_HEADERS.rst
@@ -0,0 +1,12 @@
+PRECOMPILE_HEADERS
+------------------
+
+List of header files to precompile.
+
+This property holds a :ref:`semicolon-separated list <CMake Language Lists>`
+of header files to precompile specified so far for its target.
+Use the :command:`target_precompile_headers` command to append more header
+files.
+
+This property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/release/dev/precompile-headers.rst b/Help/release/dev/precompile-headers.rst
new file mode 100644
index 0000000..8b62da7
--- /dev/null
+++ b/Help/release/dev/precompile-headers.rst
@@ -0,0 +1,6 @@
+Precompile Headers
+------------------
+
+* The :prop_tgt:`PRECOMPILE_HEADERS` target property was added to tell
+  generators to use a list of precompile headers for faster compilation
+  times.
diff --git a/Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst b/Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst
new file mode 100644
index 0000000..7c30ede
--- /dev/null
+++ b/Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst
@@ -0,0 +1,6 @@
+CMAKE_DISABLE_PRECOMPILE_HEADERS
+--------------------------------
+
+Default value for :prop_tgt:`DISABLE_PRECOMPILE_HEADERS` of targets.
+
+By default ``CMAKE_DISABLE_PRECOMPILE_HEADERS`` is ``OFF``.
-- 
cgit v0.12