From 36ded610af1bf80304a35491eeb66c34c8e9b7a8 Mon Sep 17 00:00:00 2001
From: Cristian Adam <cristian.adam@gmail.com>
Date: Sat, 5 Oct 2019 12:20:37 +0200
Subject: PCH: Generate sources during Compute step

---
 Source/cmFileAPICodemodel.cxx                  | 26 ++++++++
 Source/cmGeneratorTarget.cxx                   | 86 ++++++++++++++++++++++++++
 Source/cmGeneratorTarget.h                     |  9 +++
 Source/cmGlobalGenerator.cxx                   |  1 +
 Source/cmGlobalXCodeGenerator.cxx              |  7 ++-
 Source/cmLocalGenerator.cxx                    | 79 +++++------------------
 Source/cmLocalGenerator.h                      |  3 +-
 Source/cmLocalVisualStudio7Generator.cxx       | 18 +++++-
 Source/cmMakefileExecutableTargetGenerator.cxx |  2 -
 Source/cmMakefileLibraryTargetGenerator.cxx    |  2 -
 Source/cmMakefileTargetGenerator.cxx           | 35 ++++++++++-
 Source/cmMakefileUtilityTargetGenerator.cxx    |  2 -
 Source/cmNinjaNormalTargetGenerator.cxx        |  2 -
 Source/cmNinjaTargetGenerator.cxx              | 43 ++++++++++++-
 Source/cmVisualStudio10TargetGenerator.cxx     | 29 +++++++--
 15 files changed, 255 insertions(+), 89 deletions(-)

diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index eb50813..48561de 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -869,6 +869,32 @@ CompileData Target::BuildCompileData(cmSourceFile* sf)
     fd.Flags.emplace_back(this->ToJBT(opt));
   }
 
+  // Add precompile headers compile options.
+  const std::string pchSource =
+    this->GT->GetPchSource(this->Config, fd.Language);
+
+  if (!pchSource.empty() && !sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    std::string pchOptions;
+    if (sf->GetFullPath() == pchSource) {
+      pchOptions =
+        this->GT->GetPchCreateCompileOptions(this->Config, fd.Language);
+    } else {
+      pchOptions =
+        this->GT->GetPchUseCompileOptions(this->Config, fd.Language);
+    }
+
+    BT<std::string> tmpOpt(pchOptions);
+    tmpOpt.Value = genexInterpreter.Evaluate(tmpOpt.Value, COMPILE_OPTIONS);
+
+    // After generator evaluation we need to use the AppendCompileOptions
+    // method so we handle situations where backtrace entries have lists
+    // and properly escape flags.
+    std::string tmp;
+    lg->AppendCompileOptions(tmp, tmpOpt.Value);
+    BT<std::string> opt(tmp, tmpOpt.Backtrace);
+    fd.Flags.emplace_back(this->ToJBT(opt));
+  }
+
   // Add include directories from source file properties.
   {
     const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 3a48ff6..949d9d9 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -3486,6 +3486,92 @@ std::string cmGeneratorTarget::GetPchFileObject(const std::string& config,
   return inserted.first->second;
 }
 
+std::string cmGeneratorTarget::GetPchFile(const std::string& config,
+                                          const std::string& language)
+{
+  const auto inserted =
+    this->PchFiles.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    std::string& pchFile = inserted.first->second;
+
+    const std::string pchExtension =
+      this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
+
+    if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
+      auto replaceExtension = [](const std::string& str,
+                                 const std::string& ext) -> std::string {
+        auto dot_pos = str.rfind('.');
+        std::string result;
+        if (dot_pos != std::string::npos) {
+          result = str.substr(0, dot_pos);
+        }
+        result += ext;
+        return result;
+      };
+
+      cmGeneratorTarget* generatorTarget = this;
+      const char* pchReuseFrom =
+        generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
+      if (pchReuseFrom) {
+        generatorTarget =
+          this->GetGlobalGenerator()->FindGeneratorTarget(pchReuseFrom);
+      }
+
+      const std::string pchFileObject =
+        generatorTarget->GetPchFileObject(config, language);
+      if (!pchExtension.empty()) {
+        pchFile = replaceExtension(pchFileObject, pchExtension);
+      }
+    } else {
+      pchFile = this->GetPchHeader(config, language);
+      pchFile += pchExtension;
+    }
+  }
+  return inserted.first->second;
+}
+
+std::string cmGeneratorTarget::GetPchCreateCompileOptions(
+  const std::string& config, const std::string& language)
+{
+  const auto inserted = this->PchCreateCompileOptions.insert(
+    std::make_pair(language + config, ""));
+  if (inserted.second) {
+    std::string& createOptionList = inserted.first->second;
+
+    const std::string createOptVar =
+      cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_CREATE_PCH");
+    createOptionList = this->Makefile->GetSafeDefinition(createOptVar);
+
+    const std::string pchHeader = this->GetPchHeader(config, language);
+    const std::string pchFile = this->GetPchFile(config, language);
+
+    cmSystemTools::ReplaceString(createOptionList, "<PCH_HEADER>", pchHeader);
+    cmSystemTools::ReplaceString(createOptionList, "<PCH_FILE>", pchFile);
+  }
+  return inserted.first->second;
+}
+
+std::string cmGeneratorTarget::GetPchUseCompileOptions(
+  const std::string& config, const std::string& language)
+{
+  const auto inserted =
+    this->PchUseCompileOptions.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    std::string& useOptionList = inserted.first->second;
+
+    const std::string useOptVar =
+      cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_USE_PCH");
+    useOptionList = this->Makefile->GetSafeDefinition(useOptVar);
+
+    const std::string pchHeader = this->GetPchHeader(config, language);
+    const std::string pchFile = this->GetPchFile(config, language);
+
+    cmSystemTools::ReplaceString(useOptionList, "<PCH_HEADER>", pchHeader);
+    cmSystemTools::ReplaceString(useOptionList, "<PCH_FILE>", pchFile);
+  }
+  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 27ad6eb..1f824b1 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -464,6 +464,12 @@ public:
                            const std::string& language) const;
   std::string GetPchFileObject(const std::string& config,
                                const std::string& language);
+  std::string GetPchFile(const std::string& config,
+                         const std::string& language);
+  std::string GetPchCreateCompileOptions(const std::string& config,
+                                         const std::string& language);
+  std::string GetPchUseCompileOptions(const std::string& config,
+                                      const std::string& language);
 
   bool IsSystemIncludeDirectory(const std::string& dir,
                                 const std::string& config,
@@ -883,6 +889,9 @@ private:
   mutable std::map<std::string, std::string> PchHeaders;
   mutable std::map<std::string, std::string> PchSources;
   mutable std::map<std::string, std::string> PchObjectFiles;
+  mutable std::map<std::string, std::string> PchFiles;
+  mutable std::map<std::string, std::string> PchCreateCompileOptions;
+  mutable std::map<std::string, std::string> PchUseCompileOptions;
 
   void ExpandLinkItems(std::string const& prop, std::string const& value,
                        std::string const& config,
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index f0152c2..96656a5 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -1562,6 +1562,7 @@ bool cmGlobalGenerator::AddAutomaticSources()
         continue;
       }
       lg->AddUnityBuild(gt);
+      lg->AddPchDependencies(gt);
     }
   }
   return true;
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 8ae1e12..67f1a46 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -827,6 +827,11 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
       genexInterpreter.Evaluate(compile_defs, COMPILE_DEFINITIONS).c_str(),
       true);
   }
+
+  if (sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
+    this->AppendDefines(flagsBuild, "CMAKE_SKIP_PRECOMPILE_HEADERS", true);
+  }
+
   if (!flagsBuild.IsEmpty()) {
     if (!flags.empty()) {
       flags += ' ';
@@ -2827,8 +2832,6 @@ bool cmGlobalXCodeGenerator::CreateGroups(
         continue;
       }
 
-      generator->AddPchDependencies(gtgt, "");
-
       auto addSourceToGroup = [this, mf, gtgt,
                                &sourceGroups](std::string const& source) {
         cmSourceGroup* sourceGroup = mf->FindSourceGroup(source, sourceGroups);
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index fc027f6..a2eb1b9 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -45,7 +45,6 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
-#include <functional>
 #include <initializer_list>
 #include <iterator>
 #include <sstream>
@@ -2255,9 +2254,13 @@ void cmLocalGenerator::AppendFlagEscape(std::string& flags,
   this->AppendFlags(flags, this->EscapeForShell(rawFlag));
 }
 
-void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
-                                          const std::string& config)
+void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
 {
+  // FIXME: Handle all configurations in multi-config generators.
+  std::string config;
+  if (!this->GetGlobalGenerator()->IsMultiConfig()) {
+    config = this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
+  }
   const std::string buildType = cmSystemTools::UpperCase(config);
 
   std::vector<cmSourceFile*> sources;
@@ -2266,7 +2269,8 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
   for (const std::string& lang : { "C", "CXX" }) {
     auto langSources =
       std::count_if(sources.begin(), sources.end(), [lang](cmSourceFile* sf) {
-        return lang == sf->GetLanguage();
+        return lang == sf->GetLanguage() &&
+          !sf->GetProperty("SKIP_PRECOMPILE_HEADERS");
       });
     if (langSources == 0) {
       continue;
@@ -2279,20 +2283,10 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
       continue;
     }
 
-    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()) {
+    if (pchExtension.empty()) {
       continue;
     }
 
@@ -2301,31 +2295,17 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
 
     auto pch_sf = this->Makefile->GetOrCreateSource(
       pchSource, false, cmSourceFileLocationKind::Known);
-    std::string pchFile = pchHeader;
 
     if (!this->GetGlobalGenerator()->IsXcode()) {
       if (!pchReuseFrom) {
         target->AddSource(pchSource, true);
       }
 
+      const std::string pchFile = target->GetPchFile(config, lang);
+
       // Exclude the pch files from linking
       if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
-
-        auto replaceExtension = [](const std::string& str,
-                                   const std::string& ext) -> std::string {
-          auto dot_pos = str.rfind('.');
-          std::string result;
-          if (dot_pos != std::string::npos) {
-            result = str.substr(0, dot_pos);
-          }
-          result += ext;
-          return result;
-        };
-
         if (!pchReuseFrom) {
-          std::string pchSourceObj = target->GetPchFileObject(config, lang);
-
-          pchFile = replaceExtension(pchSourceObj, pchExtension);
           pch_sf->SetProperty("OBJECT_OUTPUTS", pchFile.c_str());
         } else {
           auto reuseTarget =
@@ -2406,14 +2386,13 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
             reuseTarget->GetPchFileObject(config, lang);
 
           // Link to the pch object file
-          target->Target->SetProperty(
+          target->Target->AppendProperty(
             "LINK_FLAGS",
-            this->ConvertToOutputFormat(pchSourceObj, SHELL).c_str());
-
-          pchFile = replaceExtension(pchSourceObj, pchExtension);
+            cmStrCat(" ", this->ConvertToOutputFormat(pchSourceObj, SHELL))
+              .c_str(),
+            true);
         }
       } else {
-        pchFile += pchExtension;
         pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str());
       }
 
@@ -2424,34 +2403,6 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
       std::string err;
       pchHeader_sf->ResolveFullPath(&err);
       target->AddSource(pchHeader);
-
-      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());
-
-    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->AppendProperty("OBJECT_DEPENDS", pchFile.c_str());
-        sf->AppendProperty("OBJECT_DEPENDS", pchHeader.c_str());
-        sf->SetProperty("COMPILE_OPTIONS", useOptionList.c_str());
-      }
     }
   }
 }
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index 7358672..12359db 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -126,8 +126,7 @@ public:
                            const std::vector<BT<std::string>>& newFlags) const;
   virtual void AppendFlagEscape(std::string& flags,
                                 const std::string& rawFlag) const;
-  void AddPchDependencies(cmGeneratorTarget* target,
-                          const std::string& config);
+  void AddPchDependencies(cmGeneratorTarget* target);
   void AddUnityBuild(cmGeneratorTarget* target);
   void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target,
                             const std::string& config,
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index e16e851..03fb565 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -1313,8 +1313,6 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout,
                                                     const std::string& libName,
                                                     cmGeneratorTarget* target)
 {
-  this->AddPchDependencies(target, "");
-
   std::vector<std::string> configs;
   this->Makefile->GetConfigurations(configs);
 
@@ -1452,6 +1450,22 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo(
         fc.CompileFlags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS));
       needfc = true;
     }
+    // Add precompile headers compile options.
+    const std::string pchSource = gt->GetPchSource(config, lang);
+    if (!pchSource.empty() && !sf.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+      std::string pchOptions;
+      if (sf.GetFullPath() == pchSource) {
+        pchOptions = gt->GetPchCreateCompileOptions(config, lang);
+      } else {
+        pchOptions = gt->GetPchUseCompileOptions(config, lang);
+      }
+
+      lg->AppendCompileOptions(
+        fc.CompileFlags,
+        genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
+      needfc = true;
+    }
+
     if (lg->FortranProject) {
       switch (cmOutputConverter::GetFortranFormat(
         sf.GetProperty("Fortran_FORMAT"))) {
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx
index 0b225cb..40265ff 100644
--- a/Source/cmMakefileExecutableTargetGenerator.cxx
+++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -40,8 +40,6 @@ 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 cf09374..54a6606 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -42,8 +42,6 @@ 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 d352f8e..51804d2 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -464,6 +464,19 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile(
   // generate the depend scanning rule
   this->WriteObjectDependRules(source, depends);
 
+  std::string config = this->LocalGenerator->GetConfigName();
+  std::string configUpper = cmSystemTools::UpperCase(config);
+
+  // Add precompile headers dependencies
+  const std::string pchSource =
+    this->GeneratorTarget->GetPchSource(config, lang);
+  if (!pchSource.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    depends.push_back(this->GeneratorTarget->GetPchHeader(config, lang));
+    if (source.GetFullPath() != pchSource) {
+      depends.push_back(this->GeneratorTarget->GetPchFile(config, lang));
+    }
+  }
+
   std::string relativeObj =
     cmStrCat(this->LocalGenerator->GetHomeRelativeOutputPath(), obj);
   // Write the build rule.
@@ -475,8 +488,6 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile(
   std::string langFlags = cmStrCat("$(", lang, "_FLAGS)");
   this->LocalGenerator->AppendFlags(flags, langFlags);
 
-  std::string config = this->LocalGenerator->GetConfigName();
-  std::string configUpper = cmSystemTools::UpperCase(config);
   cmGeneratorExpressionInterpreter genexInterpreter(
     this->LocalGenerator, config, this->GeneratorTarget, lang);
 
@@ -506,6 +517,26 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile(
                           << "\n";
   }
 
+  // Add precompile headers compile options.
+  if (!pchSource.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    std::string pchOptions;
+    if (source.GetFullPath() == pchSource) {
+      pchOptions =
+        this->GeneratorTarget->GetPchCreateCompileOptions(config, lang);
+    } else {
+      pchOptions =
+        this->GeneratorTarget->GetPchUseCompileOptions(config, lang);
+    }
+
+    const std::string& evaluatedFlags =
+      genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS);
+
+    this->LocalGenerator->AppendCompileOptions(flags, evaluatedFlags);
+    *this->FlagFileStream << "# PCH options: " << relativeObj
+                          << "_OPTIONS = " << evaluatedFlags << "\n"
+                          << "\n";
+  }
+
   // Add include directories from source file properties.
   std::vector<std::string> includes;
 
diff --git a/Source/cmMakefileUtilityTargetGenerator.cxx b/Source/cmMakefileUtilityTargetGenerator.cxx
index 516f098..1625e4f 100644
--- a/Source/cmMakefileUtilityTargetGenerator.cxx
+++ b/Source/cmMakefileUtilityTargetGenerator.cxx
@@ -25,8 +25,6 @@ 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 3f362fc..beedef4 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -61,8 +61,6 @@ 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 0fda47e..919a5db 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -141,6 +141,7 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
   cmSourceFile const* source, const std::string& language)
 {
   std::string flags = this->GetFlags(language);
+  const std::string configName = this->LocalGenerator->GetConfigName();
 
   // Add Fortran format flags.
   if (language == "Fortran") {
@@ -149,8 +150,7 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
 
   // Add source file specific flags.
   cmGeneratorExpressionInterpreter genexInterpreter(
-    this->LocalGenerator, this->LocalGenerator->GetConfigName(),
-    this->GeneratorTarget, language);
+    this->LocalGenerator, configName, this->GeneratorTarget, language);
 
   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
   if (const char* cflags = source->GetProperty(COMPILE_FLAGS)) {
@@ -164,6 +164,24 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
       flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS));
   }
 
+  // Add precompile headers compile options.
+  const std::string pchSource =
+    this->GeneratorTarget->GetPchSource(configName, language);
+
+  if (!pchSource.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    std::string pchOptions;
+    if (source->GetFullPath() == pchSource) {
+      pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions(
+        configName, language);
+    } else {
+      pchOptions =
+        this->GeneratorTarget->GetPchUseCompileOptions(configName, language);
+    }
+
+    this->LocalGenerator->AppendCompileOptions(
+      flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
+  }
+
   return flags;
 }
 
@@ -984,8 +1002,27 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
 
   objBuild.ExplicitDeps.push_back(sourceFileName);
 
+  // Add precompile headers dependencies
+  std::vector<std::string> depList;
+
+  const std::string pchSource =
+    this->GeneratorTarget->GetPchSource(this->GetConfigName(), language);
+  if (!pchSource.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    depList.push_back(
+      this->GeneratorTarget->GetPchHeader(this->GetConfigName(), language));
+    if (source->GetFullPath() != pchSource) {
+      depList.push_back(
+        this->GeneratorTarget->GetPchFile(this->GetConfigName(), language));
+    }
+  }
+
   if (const char* objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
-    std::vector<std::string> depList = cmExpandedList(objectDeps);
+    std::vector<std::string> objDepList = cmExpandedList(objectDeps);
+    std::copy(objDepList.begin(), objDepList.end(),
+              std::back_inserter(depList));
+  }
+
+  if (!depList.empty()) {
     for (std::string& odi : depList) {
       if (cmSystemTools::FileIsFullPath(odi)) {
         odi = cmSystemTools::CollapseFullPath(odi);
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 34556f9..3843bf2 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -250,8 +250,6 @@ cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator(
     this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
   this->InSourceBuild = (this->Makefile->GetCurrentSourceDirectory() ==
                          this->Makefile->GetCurrentBinaryDirectory());
-
-  this->LocalGenerator->AddPchDependencies(target, "");
 }
 
 cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator()
@@ -2330,10 +2328,28 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
         cmGeneratorExpression::Find(ccdefs) != std::string::npos;
       configDefines += ccdefs;
     }
+
+    // Add precompile headers compile options.
+    std::string customAndPchOptions = options;
+    const std::string pchSource =
+      this->GeneratorTarget->GetPchSource(config, lang);
+    if (!pchSource.empty() && !sf.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+      std::string pchOptions;
+      if (sf.GetFullPath() == pchSource) {
+        pchOptions =
+          this->GeneratorTarget->GetPchCreateCompileOptions(config, lang);
+      } else {
+        pchOptions =
+          this->GeneratorTarget->GetPchUseCompileOptions(config, lang);
+      }
+      customAndPchOptions += pchOptions;
+    }
+
     // if we have flags or defines for this config then
     // use them
     if (!flags.empty() || !options.empty() || !configDefines.empty() ||
-        !includes.empty() || compileAs || noWinRT) {
+        !includes.empty() || compileAs || noWinRT ||
+        !customAndPchOptions.empty()) {
       cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator;
       cmIDEFlagTable const* flagtable = nullptr;
       const std::string& srclang = source->GetLanguage();
@@ -2366,14 +2382,15 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
       } else {
         clOptions.Parse(flags);
       }
-      if (!options.empty()) {
+      if (!customAndPchOptions.empty()) {
         std::string expandedOptions;
         if (configDependentOptions) {
           this->LocalGenerator->AppendCompileOptions(
             expandedOptions,
-            genexInterpreter.Evaluate(options, "COMPILE_OPTIONS"));
+            genexInterpreter.Evaluate(customAndPchOptions, "COMPILE_OPTIONS"));
         } else {
-          this->LocalGenerator->AppendCompileOptions(expandedOptions, options);
+          this->LocalGenerator->AppendCompileOptions(expandedOptions,
+                                                     customAndPchOptions);
         }
         clOptions.Parse(expandedOptions);
       }
-- 
cgit v0.12