PCH: Add support for multi architecture iOS projects

Fixes: #20497
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx
index 033cb60..939f757 100644
--- a/Source/cmCommonTargetGenerator.cxx
+++ b/Source/cmCommonTargetGenerator.cxx
@@ -98,17 +98,20 @@
 }
 
 std::string cmCommonTargetGenerator::GetFlags(const std::string& l,
-                                              const std::string& config)
+                                              const std::string& config,
+                                              const std::string& arch)
 {
-  auto i = this->Configs[config].FlagsByLanguage.find(l);
-  if (i == this->Configs[config].FlagsByLanguage.end()) {
+  const std::string key = config + arch;
+
+  auto i = this->Configs[key].FlagsByLanguage.find(l);
+  if (i == this->Configs[key].FlagsByLanguage.end()) {
     std::string flags;
 
     this->LocalCommonGenerator->GetTargetCompileFlags(this->GeneratorTarget,
-                                                      config, l, flags);
+                                                      config, l, flags, arch);
 
     ByLanguageMap::value_type entry(l, flags);
-    i = this->Configs[config].FlagsByLanguage.insert(entry).first;
+    i = this->Configs[key].FlagsByLanguage.insert(entry).first;
   }
   return i->second;
 }
diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h
index b40a2ed..78cedf5 100644
--- a/Source/cmCommonTargetGenerator.h
+++ b/Source/cmCommonTargetGenerator.h
@@ -51,7 +51,8 @@
   void AppendOSXVerFlag(std::string& flags, const std::string& lang,
                         const char* name, bool so);
 
-  std::string GetFlags(const std::string& l, const std::string& config);
+  std::string GetFlags(const std::string& l, const std::string& config,
+                       const std::string& arch = std::string());
   std::string GetDefines(const std::string& l, const std::string& config);
   std::string GetIncludes(std::string const& l, const std::string& config);
   std::string GetManifests(const std::string& config);
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index 955195f..70f5847 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -872,14 +872,27 @@
   }
 
   // Add precompile headers compile options.
-  const std::string pchSource =
-    this->GT->GetPchSource(this->Config, fd.Language);
+  std::vector<std::string> architectures;
+  this->GT->GetAppleArchs(this->Config, architectures);
+  if (architectures.empty()) {
+    architectures.emplace_back();
+  }
 
-  if (!pchSource.empty() && !sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+  std::unordered_map<std::string, std::string> pchSources;
+  for (const std::string& arch : architectures) {
+    const std::string pchSource =
+      this->GT->GetPchSource(this->Config, fd.Language, arch);
+    if (!pchSource.empty()) {
+      pchSources.insert(std::make_pair(pchSource, arch));
+    }
+  }
+
+  if (!pchSources.empty() && !sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
     std::string pchOptions;
-    if (sf->ResolveFullPath() == pchSource) {
-      pchOptions =
-        this->GT->GetPchCreateCompileOptions(this->Config, fd.Language);
+    auto pchIt = pchSources.find(sf->ResolveFullPath());
+    if (pchIt != pchSources.end()) {
+      pchOptions = this->GT->GetPchCreateCompileOptions(
+        this->Config, fd.Language, pchIt->second);
     } else {
       pchOptions =
         this->GT->GetPchUseCompileOptions(this->Config, fd.Language);
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index e722034..3eb9e39 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -3539,7 +3539,8 @@
 }
 
 std::string cmGeneratorTarget::GetPchHeader(const std::string& config,
-                                            const std::string& language) const
+                                            const std::string& language,
+                                            const std::string& arch) const
 {
   if (language != "C" && language != "CXX" && language != "OBJC" &&
       language != "OBJCXX") {
@@ -3554,7 +3555,7 @@
     generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
 
   const auto inserted =
-    this->PchHeaders.insert(std::make_pair(language + config, ""));
+    this->PchHeaders.insert(std::make_pair(language + config + arch, ""));
   if (inserted.second) {
     const std::vector<BT<std::string>> headers =
       this->GetPrecompileHeaders(config, language);
@@ -3586,7 +3587,8 @@
     }
 
     filename =
-      cmStrCat(filename, "/cmake_pch", languageToExtension.at(language));
+      cmStrCat(filename, "/cmake_pch", arch.empty() ? "" : cmStrCat("_", arch),
+               languageToExtension.at(language));
 
     const std::string filename_tmp = cmStrCat(filename, ".tmp");
     if (!pchReuseFrom) {
@@ -3645,16 +3647,17 @@
 }
 
 std::string cmGeneratorTarget::GetPchSource(const std::string& config,
-                                            const std::string& language) const
+                                            const std::string& language,
+                                            const std::string& arch) const
 {
   if (language != "C" && language != "CXX" && language != "OBJC" &&
       language != "OBJCXX") {
     return std::string();
   }
   const auto inserted =
-    this->PchSources.insert(std::make_pair(language + config, ""));
+    this->PchSources.insert(std::make_pair(language + config + arch, ""));
   if (inserted.second) {
-    const std::string pchHeader = this->GetPchHeader(config, language);
+    const std::string pchHeader = this->GetPchHeader(config, language, arch);
     if (pchHeader.empty()) {
       return std::string();
     }
@@ -3681,13 +3684,15 @@
         { "OBJCXX", ".objcxx.hxx.mm" }
       };
 
-      filename += languageToExtension.at(language);
+      filename = cmStrCat(filename, arch.empty() ? "" : cmStrCat("_", arch),
+                          languageToExtension.at(language));
     } else {
       const std::map<std::string, std::string> languageToExtension = {
         { "C", ".c" }, { "CXX", ".cxx" }, { "OBJC", ".m" }, { "OBJCXX", ".mm" }
       };
 
-      filename += languageToExtension.at(language);
+      filename = cmStrCat(filename, arch.empty() ? "" : cmStrCat("_", arch),
+                          languageToExtension.at(language));
     }
 
     const std::string filename_tmp = cmStrCat(filename, ".tmp");
@@ -3704,16 +3709,17 @@
 }
 
 std::string cmGeneratorTarget::GetPchFileObject(const std::string& config,
-                                                const std::string& language)
+                                                const std::string& language,
+                                                const std::string& arch)
 {
   if (language != "C" && language != "CXX" && language != "OBJC" &&
       language != "OBJCXX") {
     return std::string();
   }
   const auto inserted =
-    this->PchObjectFiles.insert(std::make_pair(language + config, ""));
+    this->PchObjectFiles.insert(std::make_pair(language + config + arch, ""));
   if (inserted.second) {
-    const std::string pchSource = this->GetPchSource(config, language);
+    const std::string pchSource = this->GetPchSource(config, language, arch);
     if (pchSource.empty()) {
       return std::string();
     }
@@ -3730,10 +3736,11 @@
 }
 
 std::string cmGeneratorTarget::GetPchFile(const std::string& config,
-                                          const std::string& language)
+                                          const std::string& language,
+                                          const std::string& arch)
 {
   const auto inserted =
-    this->PchFiles.insert(std::make_pair(language + config, ""));
+    this->PchFiles.insert(std::make_pair(language + config + arch, ""));
   if (inserted.second) {
     std::string& pchFile = inserted.first->second;
 
@@ -3761,12 +3768,12 @@
       }
 
       const std::string pchFileObject =
-        generatorTarget->GetPchFileObject(config, language);
+        generatorTarget->GetPchFileObject(config, language, arch);
       if (!pchExtension.empty()) {
         pchFile = replaceExtension(pchFileObject, pchExtension);
       }
     } else {
-      pchFile = this->GetPchHeader(config, language);
+      pchFile = this->GetPchHeader(config, language, arch);
       pchFile += pchExtension;
     }
   }
@@ -3774,10 +3781,11 @@
 }
 
 std::string cmGeneratorTarget::GetPchCreateCompileOptions(
-  const std::string& config, const std::string& language)
+  const std::string& config, const std::string& language,
+  const std::string& arch)
 {
   const auto inserted = this->PchCreateCompileOptions.insert(
-    std::make_pair(language + config, ""));
+    std::make_pair(language + config + arch, ""));
   if (inserted.second) {
     std::string& createOptionList = inserted.first->second;
 
@@ -3792,8 +3800,8 @@
     createOptionList = cmStrCat(
       createOptionList, ";", this->Makefile->GetSafeDefinition(createOptVar));
 
-    const std::string pchHeader = this->GetPchHeader(config, language);
-    const std::string pchFile = this->GetPchFile(config, language);
+    const std::string pchHeader = this->GetPchHeader(config, language, arch);
+    const std::string pchFile = this->GetPchFile(config, language, arch);
 
     cmSystemTools::ReplaceString(createOptionList, "<PCH_HEADER>", pchHeader);
     cmSystemTools::ReplaceString(createOptionList, "<PCH_FILE>", pchFile);
@@ -3802,10 +3810,11 @@
 }
 
 std::string cmGeneratorTarget::GetPchUseCompileOptions(
-  const std::string& config, const std::string& language)
+  const std::string& config, const std::string& language,
+  const std::string& arch)
 {
-  const auto inserted =
-    this->PchUseCompileOptions.insert(std::make_pair(language + config, ""));
+  const auto inserted = this->PchUseCompileOptions.insert(
+    std::make_pair(language + config + arch, ""));
   if (inserted.second) {
     std::string& useOptionList = inserted.first->second;
 
@@ -3815,13 +3824,18 @@
     }
 
     const std::string useOptVar =
-      cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_USE_PCH");
+      cmStrCat(language, "_COMPILE_OPTIONS_USE_PCH");
 
-    useOptionList = cmStrCat(useOptionList, ";",
-                             this->Makefile->GetSafeDefinition(useOptVar));
+    const std::string useOptionListProperty = this->GetSafeProperty(useOptVar);
 
-    const std::string pchHeader = this->GetPchHeader(config, language);
-    const std::string pchFile = this->GetPchFile(config, language);
+    useOptionList = cmStrCat(
+      useOptionList, ";",
+      useOptionListProperty.empty()
+        ? this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_", useOptVar))
+        : useOptionListProperty);
+
+    const std::string pchHeader = this->GetPchHeader(config, language, arch);
+    const std::string pchFile = this->GetPchFile(config, language, arch);
 
     cmSystemTools::ReplaceString(useOptionList, "<PCH_HEADER>", pchHeader);
     cmSystemTools::ReplaceString(useOptionList, "<PCH_FILE>", pchFile);
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 12d30c5..9136928 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -478,17 +478,23 @@
     const std::string& config, const std::string& language) const;
 
   std::string GetPchHeader(const std::string& config,
-                           const std::string& language) const;
+                           const std::string& language,
+                           const std::string& arch = std::string()) const;
   std::string GetPchSource(const std::string& config,
-                           const std::string& language) const;
+                           const std::string& language,
+                           const std::string& arch = std::string()) const;
   std::string GetPchFileObject(const std::string& config,
-                               const std::string& language);
+                               const std::string& language,
+                               const std::string& arch = std::string());
   std::string GetPchFile(const std::string& config,
-                         const std::string& language);
-  std::string GetPchCreateCompileOptions(const std::string& config,
-                                         const std::string& language);
+                         const std::string& language,
+                         const std::string& arch = std::string());
+  std::string GetPchCreateCompileOptions(
+    const std::string& config, const std::string& language,
+    const std::string& arch = std::string());
   std::string GetPchUseCompileOptions(const std::string& config,
-                                      const std::string& language);
+                                      const std::string& language,
+                                      const std::string& arch = std::string());
 
   void AddSourceFileToUnityBatch(const std::string& sourceFilename);
   bool IsSourceFilePartOfUnityBatch(const std::string& sourceFilename) const;
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 8484c58..3015c8b 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -1520,16 +1520,17 @@
 void cmLocalGenerator::GetTargetCompileFlags(cmGeneratorTarget* target,
                                              std::string const& config,
                                              std::string const& lang,
-                                             std::string& flags)
+                                             std::string& flags,
+                                             std::string const& arch)
 {
   std::vector<BT<std::string>> tmpFlags =
-    this->GetTargetCompileFlags(target, config, lang);
+    this->GetTargetCompileFlags(target, config, lang, arch);
   this->AppendFlags(flags, tmpFlags);
 }
 
 std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags(
   cmGeneratorTarget* target, std::string const& config,
-  std::string const& lang)
+  std::string const& lang, std::string const& arch)
 {
   std::vector<BT<std::string>> flags;
   std::string compileFlags;
@@ -1543,7 +1544,7 @@
     this->AppendFeatureOptions(compileFlags, lang, "IPO");
   }
 
-  this->AddArchitectureFlags(compileFlags, target, lang, config);
+  this->AddArchitectureFlags(compileFlags, target, lang, config, arch);
 
   if (lang == "Fortran") {
     this->AppendFlags(compileFlags,
@@ -1771,7 +1772,8 @@
 void cmLocalGenerator::AddArchitectureFlags(std::string& flags,
                                             cmGeneratorTarget const* target,
                                             const std::string& lang,
-                                            const std::string& config)
+                                            const std::string& config,
+                                            const std::string& filterArch)
 {
   // Only add Apple specific flags on Apple platforms
   if (this->Makefile->IsOn("APPLE") && this->EmitUniversalBinaryFlags) {
@@ -1780,8 +1782,10 @@
     if (!archs.empty() && !lang.empty() &&
         (lang[0] == 'C' || lang[0] == 'F' || lang[0] == 'O')) {
       for (std::string const& arch : archs) {
-        flags += " -arch ";
-        flags += arch;
+        if (filterArch.empty() || filterArch == arch) {
+          flags += " -arch ";
+          flags += arch;
+        }
       }
     }
 
@@ -1804,10 +1808,12 @@
           if (arch_sysroots[i].empty()) {
             continue;
           }
-          flags += " -Xarch_" + archs[i] + " ";
-          // Combine sysroot flag and path to work with -Xarch
-          std::string arch_sysroot = sysrootFlag + arch_sysroots[i];
-          flags += this->ConvertToOutputFormat(arch_sysroot, SHELL);
+          if (filterArch.empty() || filterArch == archs[i]) {
+            flags += " -Xarch_" + archs[i] + " ";
+            // Combine sysroot flag and path to work with -Xarch
+            std::string arch_sysroot = sysrootFlag + arch_sysroots[i];
+            flags += this->ConvertToOutputFormat(arch_sysroot, SHELL);
+          }
         }
       } else if (sysroot && *sysroot) {
         flags += " ";
@@ -2448,146 +2454,174 @@
         continue;
       }
 
-      const std::string pchSource = target->GetPchSource(config, lang);
-      const std::string pchHeader = target->GetPchHeader(config, lang);
-
-      if (pchSource.empty() || pchHeader.empty()) {
-        continue;
-      }
-
-      const std::string pchExtension =
-        this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
-
-      if (pchExtension.empty()) {
-        continue;
-      }
-
-      const char* pchReuseFrom =
-        target->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
-
-      auto pch_sf = this->Makefile->GetOrCreateSource(
-        pchSource, false, cmSourceFileLocationKind::Known);
-
+      std::vector<std::string> architectures;
       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")) {
-          if (!pchReuseFrom) {
-            pch_sf->SetProperty("OBJECT_OUTPUTS", pchFile.c_str());
-          } else {
-            auto reuseTarget =
-              this->GlobalGenerator->FindGeneratorTarget(pchReuseFrom);
-
-            if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) {
-
-              const std::string pdb_prefix =
-                this->GetGlobalGenerator()->IsMultiConfig()
-                ? cmStrCat(this->GlobalGenerator->GetCMakeCFGIntDir(), "/")
-                : "";
-
-              const std::string target_compile_pdb_dir = cmStrCat(
-                target->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/",
-                target->GetName(), ".dir/");
-
-              const std::string copy_script =
-                cmStrCat(target_compile_pdb_dir, "copy_idb_pdb.cmake");
-              cmGeneratedFileStream file(copy_script);
-
-              file << "# CMake generated file\n";
-              for (auto extension : { ".pdb", ".idb" }) {
-                const std::string from_file =
-                  cmStrCat(reuseTarget->GetLocalGenerator()
-                             ->GetCurrentBinaryDirectory(),
-                           "/", pchReuseFrom, ".dir/${PDB_PREFIX}",
-                           pchReuseFrom, extension);
-
-                const std::string to_dir = cmStrCat(
-                  target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
-                  "/", target->GetName(), ".dir/${PDB_PREFIX}");
-
-                const std::string to_file =
-                  cmStrCat(to_dir, pchReuseFrom, extension);
-
-                std::string dest_file = to_file;
-
-                const std::string prefix = target->GetSafeProperty("PREFIX");
-                if (!prefix.empty()) {
-                  dest_file =
-                    cmStrCat(to_dir, prefix, pchReuseFrom, extension);
-                }
-
-                file << "if (EXISTS \"" << from_file << "\" AND \""
-                     << from_file << "\" IS_NEWER_THAN \"" << dest_file
-                     << "\")\n";
-                file << "  file(COPY \"" << from_file << "\""
-                     << " DESTINATION \"" << to_dir << "\")\n";
-                if (!prefix.empty()) {
-                  file << "  file(REMOVE \"" << dest_file << "\")\n";
-                  file << "  file(RENAME \"" << to_file << "\" \"" << dest_file
-                       << "\")\n";
-                }
-                file << "endif()\n";
-              }
-
-              cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
-                { cmSystemTools::GetCMakeCommand(),
-                  cmStrCat("-DPDB_PREFIX=", pdb_prefix), "-P", copy_script });
-
-              const std::string no_main_dependency;
-              const std::vector<std::string> no_deps;
-              const char* no_message = "";
-              const char* no_current_dir = nullptr;
-              std::vector<std::string> no_byproducts;
-
-              std::vector<std::string> outputs;
-              outputs.push_back(cmStrCat(target_compile_pdb_dir, pdb_prefix,
-                                         pchReuseFrom, ".pdb"));
-
-              if (this->GetGlobalGenerator()->IsVisualStudio()) {
-                this->AddCustomCommandToTarget(
-                  target->GetName(), outputs, no_deps, commandLines,
-                  cmCustomCommandType::PRE_BUILD, no_message, no_current_dir);
-              } else {
-                cmImplicitDependsList no_implicit_depends;
-                cmSourceFile* copy_rule = this->AddCustomCommandToOutput(
-                  outputs, no_byproducts, no_deps, no_main_dependency,
-                  no_implicit_depends, commandLines, no_message,
-                  no_current_dir);
-
-                if (copy_rule) {
-                  target->AddSource(copy_rule->ResolveFullPath());
-                }
-              }
-
-              target->Target->SetProperty("COMPILE_PDB_OUTPUT_DIRECTORY",
-                                          target_compile_pdb_dir);
-            }
-
-            std::string pchSourceObj =
-              reuseTarget->GetPchFileObject(config, lang);
-
-            // Link to the pch object file
-            target->Target->AppendProperty(
-              "LINK_FLAGS",
-              cmStrCat(" ", this->ConvertToOutputFormat(pchSourceObj, SHELL)),
-              true);
+        target->GetAppleArchs(config, architectures);
+      }
+      if (architectures.empty()) {
+        architectures.emplace_back();
+      } else {
+        std::string useMultiArchPch;
+        for (const std::string& arch : architectures) {
+          const std::string pchHeader =
+            target->GetPchHeader(config, lang, arch);
+          if (!pchHeader.empty()) {
+            useMultiArchPch = cmStrCat(useMultiArchPch, ";-Xarch_", arch,
+                                       ";-include", pchHeader);
           }
-        } else {
-          pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str());
         }
 
-        // Add pchHeader to source files, which will
-        // be grouped as "Precompile Header File"
-        auto pchHeader_sf = this->Makefile->GetOrCreateSource(
-          pchHeader, false, cmSourceFileLocationKind::Known);
-        std::string err;
-        pchHeader_sf->ResolveFullPath(&err);
-        target->AddSource(pchHeader);
+        if (!useMultiArchPch.empty()) {
+          target->Target->SetProperty(
+            cmStrCat(lang, "_COMPILE_OPTIONS_USE_PCH"), useMultiArchPch);
+        }
+      }
+
+      for (const std::string& arch : architectures) {
+        const std::string pchSource = target->GetPchSource(config, lang, arch);
+        const std::string pchHeader = target->GetPchHeader(config, lang, arch);
+
+        if (pchSource.empty() || pchHeader.empty()) {
+          continue;
+        }
+
+        const std::string pchExtension =
+          this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
+
+        if (pchExtension.empty()) {
+          continue;
+        }
+
+        const char* pchReuseFrom =
+          target->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
+
+        auto pch_sf = this->Makefile->GetOrCreateSource(
+          pchSource, false, cmSourceFileLocationKind::Known);
+
+        if (!this->GetGlobalGenerator()->IsXcode()) {
+          if (!pchReuseFrom) {
+            target->AddSource(pchSource, true);
+          }
+
+          const std::string pchFile = target->GetPchFile(config, lang, arch);
+
+          // Exclude the pch files from linking
+          if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
+            if (!pchReuseFrom) {
+              pch_sf->SetProperty("OBJECT_OUTPUTS", pchFile.c_str());
+            } else {
+              auto reuseTarget =
+                this->GlobalGenerator->FindGeneratorTarget(pchReuseFrom);
+
+              if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) {
+
+                const std::string pdb_prefix =
+                  this->GetGlobalGenerator()->IsMultiConfig()
+                  ? cmStrCat(this->GlobalGenerator->GetCMakeCFGIntDir(), "/")
+                  : "";
+
+                const std::string target_compile_pdb_dir = cmStrCat(
+                  target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
+                  "/", target->GetName(), ".dir/");
+
+                const std::string copy_script =
+                  cmStrCat(target_compile_pdb_dir, "copy_idb_pdb.cmake");
+                cmGeneratedFileStream file(copy_script);
+
+                file << "# CMake generated file\n";
+                for (auto extension : { ".pdb", ".idb" }) {
+                  const std::string from_file =
+                    cmStrCat(reuseTarget->GetLocalGenerator()
+                               ->GetCurrentBinaryDirectory(),
+                             "/", pchReuseFrom, ".dir/${PDB_PREFIX}",
+                             pchReuseFrom, extension);
+
+                  const std::string to_dir = cmStrCat(
+                    target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
+                    "/", target->GetName(), ".dir/${PDB_PREFIX}");
+
+                  const std::string to_file =
+                    cmStrCat(to_dir, pchReuseFrom, extension);
+
+                  std::string dest_file = to_file;
+
+                  const std::string prefix = target->GetSafeProperty("PREFIX");
+                  if (!prefix.empty()) {
+                    dest_file =
+                      cmStrCat(to_dir, prefix, pchReuseFrom, extension);
+                  }
+
+                  file << "if (EXISTS \"" << from_file << "\" AND \""
+                       << from_file << "\" IS_NEWER_THAN \"" << dest_file
+                       << "\")\n";
+                  file << "  file(COPY \"" << from_file << "\""
+                       << " DESTINATION \"" << to_dir << "\")\n";
+                  if (!prefix.empty()) {
+                    file << "  file(REMOVE \"" << dest_file << "\")\n";
+                    file << "  file(RENAME \"" << to_file << "\" \""
+                         << dest_file << "\")\n";
+                  }
+                  file << "endif()\n";
+                }
+
+                cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
+                  { cmSystemTools::GetCMakeCommand(),
+                    cmStrCat("-DPDB_PREFIX=", pdb_prefix), "-P",
+                    copy_script });
+
+                const std::string no_main_dependency;
+                const std::vector<std::string> no_deps;
+                const char* no_message = "";
+                const char* no_current_dir = nullptr;
+                std::vector<std::string> no_byproducts;
+
+                std::vector<std::string> outputs;
+                outputs.push_back(cmStrCat(target_compile_pdb_dir, pdb_prefix,
+                                           pchReuseFrom, ".pdb"));
+
+                if (this->GetGlobalGenerator()->IsVisualStudio()) {
+                  this->AddCustomCommandToTarget(
+                    target->GetName(), outputs, no_deps, commandLines,
+                    cmCustomCommandType::PRE_BUILD, no_message,
+                    no_current_dir);
+                } else {
+                  cmImplicitDependsList no_implicit_depends;
+                  cmSourceFile* copy_rule = this->AddCustomCommandToOutput(
+                    outputs, no_byproducts, no_deps, no_main_dependency,
+                    no_implicit_depends, commandLines, no_message,
+                    no_current_dir);
+
+                  if (copy_rule) {
+                    target->AddSource(copy_rule->ResolveFullPath());
+                  }
+                }
+
+                target->Target->SetProperty("COMPILE_PDB_OUTPUT_DIRECTORY",
+                                            target_compile_pdb_dir);
+              }
+
+              std::string pchSourceObj =
+                reuseTarget->GetPchFileObject(config, lang, arch);
+
+              // Link to the pch object file
+              target->Target->AppendProperty(
+                "LINK_FLAGS",
+                cmStrCat(" ",
+                         this->ConvertToOutputFormat(pchSourceObj, SHELL)),
+                true);
+            }
+          } else {
+            pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str());
+          }
+
+          // Add pchHeader to source files, which will
+          // be grouped as "Precompile Header File"
+          auto pchHeader_sf = this->Makefile->GetOrCreateSource(
+            pchHeader, false, cmSourceFileLocationKind::Known);
+          std::string err;
+          pchHeader_sf->ResolveFullPath(&err);
+          target->AddSource(pchHeader);
+        }
       }
     }
   }
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index 88194b7..25ed265 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -105,8 +105,8 @@
 
   void AddArchitectureFlags(std::string& flags,
                             cmGeneratorTarget const* target,
-                            const std::string& lang,
-                            const std::string& config);
+                            const std::string& lang, const std::string& config,
+                            const std::string& filterArch = std::string());
 
   void AddLanguageFlags(std::string& flags, cmGeneratorTarget const* target,
                         const std::string& lang, const std::string& config);
@@ -435,10 +435,11 @@
                                              std::string const& lang) const;
   void GetTargetCompileFlags(cmGeneratorTarget* target,
                              std::string const& config,
-                             std::string const& lang, std::string& flags);
-  std::vector<BT<std::string>> GetTargetCompileFlags(cmGeneratorTarget* target,
-                                                     std::string const& config,
-                                                     std::string const& lang);
+                             std::string const& lang, std::string& flags,
+                             std::string const& arch = std::string());
+  std::vector<BT<std::string>> GetTargetCompileFlags(
+    cmGeneratorTarget* target, std::string const& config,
+    std::string const& lang, std::string const& arch = std::string());
 
   std::string GetFrameworkFlags(std::string const& l,
                                 std::string const& config,
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 4fb84ee..b7dac22 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -5,6 +5,7 @@
 #include <cassert>
 #include <cstdio>
 #include <sstream>
+#include <unordered_map>
 #include <utility>
 
 #include <cm/memory>
@@ -341,16 +342,25 @@
   }
 
   for (std::string const& language : languages) {
-    std::string flags = this->GetFlags(language, this->GetConfigName());
     std::string defines = this->GetDefines(language, this->GetConfigName());
     std::string includes = this->GetIncludes(language, this->GetConfigName());
     // Escape comment characters so they do not terminate assignment.
-    cmSystemTools::ReplaceString(flags, "#", "\\#");
     cmSystemTools::ReplaceString(defines, "#", "\\#");
     cmSystemTools::ReplaceString(includes, "#", "\\#");
-    *this->FlagFileStream << language << "_FLAGS = " << flags << "\n\n";
     *this->FlagFileStream << language << "_DEFINES = " << defines << "\n\n";
     *this->FlagFileStream << language << "_INCLUDES = " << includes << "\n\n";
+
+    std::vector<std::string> architectures;
+    this->GeneratorTarget->GetAppleArchs(this->GetConfigName(), architectures);
+    architectures.emplace_back();
+
+    for (const std::string& arch : architectures) {
+      std::string flags =
+        this->GetFlags(language, this->GetConfigName(), arch);
+      cmSystemTools::ReplaceString(flags, "#", "\\#");
+      *this->FlagFileStream << language << "_FLAGS" << arch << " = " << flags
+                            << "\n\n";
+    }
   }
 }
 
@@ -463,17 +473,37 @@
   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")) {
-    std::string const& pchHeader =
-      this->GeneratorTarget->GetPchHeader(config, lang);
-    depends.push_back(pchHeader);
-    if (source.GetFullPath() != pchSource) {
-      depends.push_back(this->GeneratorTarget->GetPchFile(config, lang));
+  std::vector<std::string> architectures;
+  this->GeneratorTarget->GetAppleArchs(config, architectures);
+  if (architectures.empty()) {
+    architectures.emplace_back();
+  }
+
+  std::string filterArch;
+  std::unordered_map<std::string, std::string> pchSources;
+  for (const std::string& arch : architectures) {
+    const std::string pchSource =
+      this->GeneratorTarget->GetPchSource(config, lang, arch);
+    if (pchSource == source.GetFullPath()) {
+      filterArch = arch;
     }
-    this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, lang,
-                                             objFullPath, pchHeader);
+    if (!pchSource.empty()) {
+      pchSources.insert(std::make_pair(pchSource, arch));
+    }
+  }
+
+  if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    for (const std::string& arch : architectures) {
+      std::string const& pchHeader =
+        this->GeneratorTarget->GetPchHeader(config, lang, arch);
+      depends.push_back(pchHeader);
+      if (pchSources.find(source.GetFullPath()) == pchSources.end()) {
+        depends.push_back(
+          this->GeneratorTarget->GetPchFile(config, lang, arch));
+      }
+      this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, lang,
+                                               objFullPath, pchHeader);
+    }
   }
 
   std::string relativeObj =
@@ -484,7 +514,7 @@
   std::string flags;
 
   // Add language-specific flags.
-  std::string langFlags = cmStrCat("$(", lang, "_FLAGS)");
+  std::string langFlags = cmStrCat("$(", lang, "_FLAGS", filterArch, ")");
   this->LocalGenerator->AppendFlags(flags, langFlags);
 
   cmGeneratorExpressionInterpreter genexInterpreter(
@@ -517,11 +547,12 @@
   }
 
   // Add precompile headers compile options.
-  if (!pchSource.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+  if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
     std::string pchOptions;
-    if (source.GetFullPath() == pchSource) {
-      pchOptions =
-        this->GeneratorTarget->GetPchCreateCompileOptions(config, lang);
+    auto pchIt = pchSources.find(source.GetFullPath());
+    if (pchIt != pchSources.end()) {
+      pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions(
+        config, lang, pchIt->second);
     } else {
       pchOptions =
         this->GeneratorTarget->GetPchUseCompileOptions(config, lang);
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 8833fa4..ed74cf9 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -7,6 +7,8 @@
 #include <iterator>
 #include <map>
 #include <ostream>
+#include <unordered_map>
+#include <unordered_set>
 #include <utility>
 
 #include <cm/memory>
@@ -157,7 +159,26 @@
   cmSourceFile const* source, const std::string& language,
   const std::string& config)
 {
-  std::string flags = this->GetFlags(language, config);
+  std::vector<std::string> architectures;
+  std::unordered_map<std::string, std::string> pchSources;
+  this->GeneratorTarget->GetAppleArchs(config, architectures);
+  if (architectures.empty()) {
+    architectures.emplace_back();
+  }
+
+  std::string filterArch;
+  for (const std::string& arch : architectures) {
+    const std::string pchSource =
+      this->GeneratorTarget->GetPchSource(config, language, arch);
+    if (pchSource == source->GetFullPath()) {
+      filterArch = arch;
+    }
+    if (!pchSource.empty()) {
+      pchSources.insert(std::make_pair(pchSource, arch));
+    }
+  }
+
+  std::string flags = this->GetFlags(language, config, filterArch);
 
   // Add Fortran format flags.
   if (language == "Fortran") {
@@ -181,14 +202,12 @@
   }
 
   // Add precompile headers compile options.
-  const std::string pchSource =
-    this->GeneratorTarget->GetPchSource(config, language);
-
-  if (!pchSource.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+  if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
     std::string pchOptions;
-    if (source->GetFullPath() == pchSource) {
-      pchOptions =
-        this->GeneratorTarget->GetPchCreateCompileOptions(config, language);
+    auto pchIt = pchSources.find(source->GetFullPath());
+    if (pchIt != pchSources.end()) {
+      pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions(
+        config, language, pchIt->second);
     } else {
       pchOptions =
         this->GeneratorTarget->GetPchUseCompileOptions(config, language);
@@ -1050,12 +1069,30 @@
   // Add precompile headers dependencies
   std::vector<std::string> depList;
 
-  const std::string pchSource =
-    this->GeneratorTarget->GetPchSource(config, language);
-  if (!pchSource.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
-    depList.push_back(this->GeneratorTarget->GetPchHeader(config, language));
-    if (source->GetFullPath() != pchSource) {
-      depList.push_back(this->GeneratorTarget->GetPchFile(config, language));
+  std::vector<std::string> architectures;
+  this->GeneratorTarget->GetAppleArchs(config, architectures);
+  if (architectures.empty()) {
+    architectures.emplace_back();
+  }
+
+  std::unordered_set<std::string> pchSources;
+  for (const std::string& arch : architectures) {
+    const std::string pchSource =
+      this->GeneratorTarget->GetPchSource(config, language, arch);
+
+    if (!pchSource.empty()) {
+      pchSources.insert(pchSource);
+    }
+  }
+
+  if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    for (const std::string& arch : architectures) {
+      depList.push_back(
+        this->GeneratorTarget->GetPchHeader(config, language, arch));
+      if (pchSources.find(source->GetFullPath()) == pchSources.end()) {
+        depList.push_back(
+          this->GeneratorTarget->GetPchFile(config, language, arch));
+      }
     }
   }
 
@@ -1208,8 +1245,9 @@
   this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
                              vars);
 
-  if (!pchSource.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
-    if (source->GetFullPath() == pchSource) {
+  if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+    auto pchIt = pchSources.find(source->GetFullPath());
+    if (pchIt != pchSources.end()) {
       this->addPoolNinjaVariable("JOB_POOL_PRECOMPILE_HEADER",
                                  this->GetGeneratorTarget(), vars);
     }
diff --git a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
index 3f684fc..381b800 100644
--- a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
+++ b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
@@ -4,12 +4,9 @@
 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()
+  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)
 endfunction()
 
 run_cmake(DisabledPch)