瀏覽代碼

pchreuse: defer target existence enforcement to generation time

Now that generation can work with any way the state gets to the way it
is, just do the target enforcement at generation time. This allows PCH
reuse targets to be declared before or after targets which use them.

Also update `cmLocalGenerator` to use the methods now that they reliably
provide values rather than parallel construction.
Ben Boeckel 4 月之前
父節點
當前提交
f78f592b78

+ 78 - 12
Source/cmGeneratorTarget.cxx

@@ -1296,6 +1296,8 @@ std::string cmGeneratorTarget::GetCompilePDBName(
     return components.prefix + pdbName + ".pdb";
     return components.prefix + pdbName + ".pdb";
   }
   }
 
 
+  // If the target is PCH-reused, we need a stable name for the PDB file so
+  // that reusing targets can construct a stable name for it.
   if (this->PchReused) {
   if (this->PchReused) {
     NameComponents const& components = GetFullNameInternalComponents(
     NameComponents const& components = GetFullNameInternalComponents(
       config, cmStateEnums::RuntimeBinaryArtifact);
       config, cmStateEnums::RuntimeBinaryArtifact);
@@ -2811,12 +2813,45 @@ std::string cmGeneratorTarget::GetClangTidyExportFixesDirectory(
   return cmSystemTools::CollapseFullPath(path);
   return cmSystemTools::CollapseFullPath(path);
 }
 }
 
 
+struct CycleWatcher
+{
+  CycleWatcher(bool& flag)
+    : Flag(flag)
+  {
+    this->Flag = true;
+  }
+  ~CycleWatcher() { this->Flag = false; }
+  bool& Flag;
+};
+
 cmGeneratorTarget const* cmGeneratorTarget::GetPchReuseTarget() const
 cmGeneratorTarget const* cmGeneratorTarget::GetPchReuseTarget() const
 {
 {
+  if (this->ComputingPchReuse) {
+    // TODO: Get the full cycle.
+    if (!this->PchReuseCycleDetected) {
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Circular PCH reuse target involving '", this->GetName(),
+                 '\''));
+    }
+    this->PchReuseCycleDetected = true;
+    return nullptr;
+  }
+  CycleWatcher watch(this->ComputingPchReuse);
+  (void)watch;
   cmValue pchReuseFrom = this->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
   cmValue pchReuseFrom = this->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
   if (!pchReuseFrom) {
   if (!pchReuseFrom) {
     return nullptr;
     return nullptr;
   }
   }
+  cmGeneratorTarget const* generatorTarget =
+    this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
+  if (!generatorTarget) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat(
+        "Target \"", *pchReuseFrom, "\" for the \"", this->GetName(),
+        R"(" target's "PRECOMPILE_HEADERS_REUSE_FROM" property does not exist.)"));
+  }
   if (this->GetProperty("PRECOMPILE_HEADERS").IsOn()) {
   if (this->GetProperty("PRECOMPILE_HEADERS").IsOn()) {
     this->Makefile->IssueMessage(
     this->Makefile->IssueMessage(
       MessageType::FATAL_ERROR,
       MessageType::FATAL_ERROR,
@@ -2824,16 +2859,43 @@ cmGeneratorTarget const* cmGeneratorTarget::GetPchReuseTarget() const
                this->GetName(), "\")\n"));
                this->GetName(), "\")\n"));
   }
   }
 
 
-  // Guaranteed to exist because `SetProperty` does a target lookup.
-  return this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
+  if (generatorTarget) {
+    if (auto const* recurseReuseTarget =
+          generatorTarget->GetPchReuseTarget()) {
+      return recurseReuseTarget;
+    }
+  }
+  return generatorTarget;
 }
 }
 
 
 cmGeneratorTarget* cmGeneratorTarget::GetPchReuseTarget()
 cmGeneratorTarget* cmGeneratorTarget::GetPchReuseTarget()
 {
 {
+  if (this->ComputingPchReuse) {
+    // TODO: Get the full cycle.
+    if (!this->PchReuseCycleDetected) {
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Circular PCH reuse target involving '", this->GetName(),
+                 '\''));
+    }
+    this->PchReuseCycleDetected = true;
+    return nullptr;
+  }
+  CycleWatcher watch(this->ComputingPchReuse);
+  (void)watch;
   cmValue pchReuseFrom = this->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
   cmValue pchReuseFrom = this->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
   if (!pchReuseFrom) {
   if (!pchReuseFrom) {
     return nullptr;
     return nullptr;
   }
   }
+  cmGeneratorTarget* generatorTarget =
+    this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
+  if (!generatorTarget) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat(
+        "Target \"", *pchReuseFrom, "\" for the \"", this->GetName(),
+        R"(" target's "PRECOMPILE_HEADERS_REUSE_FROM" property does not exist.)"));
+  }
   if (this->GetProperty("PRECOMPILE_HEADERS").IsOn()) {
   if (this->GetProperty("PRECOMPILE_HEADERS").IsOn()) {
     this->Makefile->IssueMessage(
     this->Makefile->IssueMessage(
       MessageType::FATAL_ERROR,
       MessageType::FATAL_ERROR,
@@ -2841,8 +2903,12 @@ cmGeneratorTarget* cmGeneratorTarget::GetPchReuseTarget()
                this->GetName(), "\")\n"));
                this->GetName(), "\")\n"));
   }
   }
 
 
-  // Guaranteed to exist because `SetProperty` does a target lookup.
-  return this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
+  if (generatorTarget) {
+    if (auto* recurseReuseTarget = generatorTarget->GetPchReuseTarget()) {
+      return recurseReuseTarget;
+    }
+  }
+  return generatorTarget;
 }
 }
 
 
 std::vector<std::string> cmGeneratorTarget::GetPchArchs(
 std::vector<std::string> cmGeneratorTarget::GetPchArchs(
@@ -2873,6 +2939,7 @@ std::string cmGeneratorTarget::GetPchHeader(std::string const& config,
   }
   }
   cmGeneratorTarget const* generatorTarget = this;
   cmGeneratorTarget const* generatorTarget = this;
   cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget();
   cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget();
+  bool const haveReuseTarget = reuseTarget && reuseTarget != this;
   if (reuseTarget) {
   if (reuseTarget) {
     generatorTarget = reuseTarget;
     generatorTarget = reuseTarget;
   }
   }
@@ -2882,7 +2949,7 @@ std::string cmGeneratorTarget::GetPchHeader(std::string const& config,
   if (inserted.second) {
   if (inserted.second) {
     std::vector<BT<std::string>> const headers =
     std::vector<BT<std::string>> const headers =
       this->GetPrecompileHeaders(config, language);
       this->GetPrecompileHeaders(config, language);
-    if (headers.empty() && !reuseTarget) {
+    if (headers.empty() && !haveReuseTarget) {
       return std::string();
       return std::string();
     }
     }
     std::string& filename = inserted.first->second;
     std::string& filename = inserted.first->second;
@@ -2905,7 +2972,7 @@ std::string cmGeneratorTarget::GetPchHeader(std::string const& config,
                languageToExtension.at(language));
                languageToExtension.at(language));
 
 
     std::string const filename_tmp = cmStrCat(filename, ".tmp");
     std::string const filename_tmp = cmStrCat(filename, ".tmp");
-    if (!reuseTarget) {
+    if (!haveReuseTarget) {
       cmValue pchPrologue =
       cmValue pchPrologue =
         this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
         this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
       cmValue pchEpilogue =
       cmValue pchEpilogue =
@@ -2981,6 +3048,7 @@ std::string cmGeneratorTarget::GetPchSource(std::string const& config,
 
 
     cmGeneratorTarget const* generatorTarget = this;
     cmGeneratorTarget const* generatorTarget = this;
     cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget();
     cmGeneratorTarget const* reuseTarget = this->GetPchReuseTarget();
+    bool const haveReuseTarget = reuseTarget && reuseTarget != this;
     if (reuseTarget) {
     if (reuseTarget) {
       generatorTarget = reuseTarget;
       generatorTarget = reuseTarget;
     }
     }
@@ -3009,7 +3077,7 @@ std::string cmGeneratorTarget::GetPchSource(std::string const& config,
     }
     }
 
 
     std::string const filename_tmp = cmStrCat(filename, ".tmp");
     std::string const filename_tmp = cmStrCat(filename, ".tmp");
-    if (!reuseTarget) {
+    if (!haveReuseTarget) {
       {
       {
         cmGeneratedFileStream file(filename_tmp);
         cmGeneratedFileStream file(filename_tmp);
         file << "/* generated by CMake */\n";
         file << "/* generated by CMake */\n";
@@ -4467,11 +4535,9 @@ bool cmGeneratorTarget::ComputePDBOutputDir(std::string const& kind,
     }
     }
   }
   }
   if (out.empty()) {
   if (out.empty()) {
-    // A target which is PCH-reused must have a stable PDB output directory so
-    // that the PDB can be stably referred to when consuming the PCH file.
-    if (this->PchReused) {
-      out = cmStrCat(this->GetLocalGenerator()->GetCurrentBinaryDirectory(),
-                     '/', this->GetName(), ".dir/");
+    // Compile output should always have a path.
+    if (kind == "COMPILE_PDB"_s) {
+      out = this->GetSupportDirectory();
     } else {
     } else {
       return false;
       return false;
     }
     }

+ 2 - 0
Source/cmGeneratorTarget.h

@@ -1522,6 +1522,8 @@ private:
   };
   };
   mutable std::map<std::string, InfoByConfig> Configs;
   mutable std::map<std::string, InfoByConfig> Configs;
   bool PchReused = false;
   bool PchReused = false;
+  mutable bool ComputingPchReuse = false;
+  mutable bool PchReuseCycleDetected = false;
 };
 };
 
 
 class cmGeneratorTarget::TargetPropertyEntry
 class cmGeneratorTarget::TargetPropertyEntry

+ 40 - 59
Source/cmLocalGenerator.cxx

@@ -2779,8 +2779,8 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
           continue;
           continue;
         }
         }
 
 
-        cmValue ReuseFrom =
-          target->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
+        auto* reuseTarget = target->GetPchReuseTarget();
+        bool const haveReuseTarget = reuseTarget && reuseTarget != target;
 
 
         auto* pch_sf = this->Makefile->GetOrCreateSource(
         auto* pch_sf = this->Makefile->GetOrCreateSource(
           pchSource, false, cmSourceFileLocationKind::Known);
           pchSource, false, cmSourceFileLocationKind::Known);
@@ -2789,7 +2789,7 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
         pch_sf->SetProperty("CXX_SCAN_FOR_MODULES", "0");
         pch_sf->SetProperty("CXX_SCAN_FOR_MODULES", "0");
 
 
         if (!this->GetGlobalGenerator()->IsXcode()) {
         if (!this->GetGlobalGenerator()->IsXcode()) {
-          if (!ReuseFrom) {
+          if (!haveReuseTarget) {
             target->AddSource(pchSource, true);
             target->AddSource(pchSource, true);
           }
           }
 
 
@@ -2797,14 +2797,11 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
 
 
           // Exclude the pch files from linking
           // Exclude the pch files from linking
           if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
           if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
-            if (!ReuseFrom) {
+            if (!haveReuseTarget) {
               pch_sf->AppendProperty(
               pch_sf->AppendProperty(
                 "OBJECT_OUTPUTS",
                 "OBJECT_OUTPUTS",
                 cmStrCat("$<$<CONFIG:", config, ">:", pchFile, '>'));
                 cmStrCat("$<$<CONFIG:", config, ">:", pchFile, '>'));
             } else {
             } else {
-              auto* reuseTarget =
-                this->GlobalGenerator->FindGeneratorTarget(*ReuseFrom);
-
               if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) {
               if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) {
 
 
                 std::string const compilerId =
                 std::string const compilerId =
@@ -2850,11 +2847,11 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
                 }
                 }
 
 
                 if (editAndContinueDebugInfo || msvc2008OrLess) {
                 if (editAndContinueDebugInfo || msvc2008OrLess) {
-                  this->CopyPchCompilePdb(config, lang, target, *ReuseFrom,
-                                          reuseTarget, { ".pdb", ".idb" });
+                  this->CopyPchCompilePdb(config, lang, target, reuseTarget,
+                                          { ".pdb", ".idb" });
                 } else if (programDatabaseDebugInfo) {
                 } else if (programDatabaseDebugInfo) {
-                  this->CopyPchCompilePdb(config, lang, target, *ReuseFrom,
-                                          reuseTarget, { ".pdb" });
+                  this->CopyPchCompilePdb(config, lang, target, reuseTarget,
+                                          { ".pdb" });
                 }
                 }
               }
               }
 
 
@@ -2906,18 +2903,11 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
 
 
 void cmLocalGenerator::CopyPchCompilePdb(
 void cmLocalGenerator::CopyPchCompilePdb(
   std::string const& config, std::string const& language,
   std::string const& config, std::string const& language,
-  cmGeneratorTarget* target, std::string const& ReuseFrom,
-  cmGeneratorTarget* reuseTarget, std::vector<std::string> const& extensions)
+  cmGeneratorTarget* target, cmGeneratorTarget* reuseTarget,
+  std::vector<std::string> const& extensions)
 {
 {
-  std::string const pdb_prefix =
-    this->GetGlobalGenerator()->IsMultiConfig() ? cmStrCat(config, '/') : "";
-
-  std::string const target_compile_pdb_dir =
-    cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/',
-             target->GetName(), ".dir/");
-
-  std::string const copy_script =
-    cmStrCat(target_compile_pdb_dir, "copy_idb_pdb_", config, ".cmake");
+  std::string const copy_script = cmStrCat(target->GetSupportDirectory(),
+                                           "/copy_idb_pdb_", config, ".cmake");
   cmGeneratedFileStream file(copy_script);
   cmGeneratedFileStream file(copy_script);
 
 
   file << "# CMake generated file\n";
   file << "# CMake generated file\n";
@@ -2926,28 +2916,37 @@ void cmLocalGenerator::CopyPchCompilePdb(
        << "# by mspdbsrv. The foreach retry loop is needed to make sure\n"
        << "# by mspdbsrv. The foreach retry loop is needed to make sure\n"
        << "# the pdb file is ready to be copied.\n\n";
        << "# the pdb file is ready to be copied.\n\n";
 
 
+  auto configGenex = [&](cm::string_view expr) -> std::string {
+    if (this->GetGlobalGenerator()->IsMultiConfig()) {
+      return cmStrCat("$<$<CONFIG:", config, ">:", expr, '>');
+    }
+    return std::string(expr);
+  };
+
+  std::vector<std::string> outputs;
+  auto replaceExtension = [](std::string const& path,
+                             std::string const& ext) -> std::string {
+    auto const dir = cmSystemTools::GetFilenamePath(path);
+    auto const base = cmSystemTools::GetFilenameWithoutLastExtension(path);
+    if (dir.empty()) {
+      return cmStrCat(base, ext);
+    }
+    return cmStrCat(dir, '/', base, ext);
+  };
   for (auto const& extension : extensions) {
   for (auto const& extension : extensions) {
     std::string const from_file =
     std::string const from_file =
-      cmStrCat(reuseTarget->GetLocalGenerator()->GetCurrentBinaryDirectory(),
-               '/', ReuseFrom, ".dir/${PDB_PREFIX}", ReuseFrom, extension);
-
-    std::string const to_dir =
-      cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/',
-               target->GetName(), ".dir/${PDB_PREFIX}");
-
-    std::string const to_file = cmStrCat(to_dir, ReuseFrom, extension);
-
-    std::string dest_file = to_file;
-
-    std::string const& prefix = target->GetSafeProperty("PREFIX");
-    if (!prefix.empty()) {
-      dest_file = cmStrCat(to_dir, prefix, ReuseFrom, extension);
-    }
+      replaceExtension(reuseTarget->GetCompilePDBPath(config), extension);
+    std::string const to_dir = target->GetCompilePDBDirectory(config);
+    std::string const to_file = cmStrCat(
+      replaceExtension(reuseTarget->GetCompilePDBName(config), extension),
+      '/');
+    std::string const dest_file = cmStrCat(to_dir, to_file);
 
 
     file << "foreach(retry RANGE 1 30)\n";
     file << "foreach(retry RANGE 1 30)\n";
     file << "  if (EXISTS \"" << from_file << "\" AND (NOT EXISTS \""
     file << "  if (EXISTS \"" << from_file << "\" AND (NOT EXISTS \""
          << dest_file << "\" OR NOT \"" << dest_file << "\" IS_NEWER_THAN \""
          << dest_file << "\" OR NOT \"" << dest_file << "\" IS_NEWER_THAN \""
          << from_file << "\"))\n";
          << from_file << "\"))\n";
+    file << "    file(MAKE_DIRECTORY \"" << to_dir << "\")\n";
     file << "    execute_process(COMMAND ${CMAKE_COMMAND} -E copy";
     file << "    execute_process(COMMAND ${CMAKE_COMMAND} -E copy";
     file << " \"" << from_file << "\""
     file << " \"" << from_file << "\""
          << " \"" << to_dir << "\" RESULT_VARIABLE result "
          << " \"" << to_dir << "\" RESULT_VARIABLE result "
@@ -2956,10 +2955,6 @@ void cmLocalGenerator::CopyPchCompilePdb(
          << "      execute_process(COMMAND ${CMAKE_COMMAND}"
          << "      execute_process(COMMAND ${CMAKE_COMMAND}"
          << " -E sleep 1)\n"
          << " -E sleep 1)\n"
          << "    else()\n";
          << "    else()\n";
-    if (!prefix.empty()) {
-      file << "  file(REMOVE \"" << dest_file << "\")\n";
-      file << "  file(RENAME \"" << to_file << "\" \"" << dest_file << "\")\n";
-    }
     file << "      break()\n"
     file << "      break()\n"
          << "    endif()\n";
          << "    endif()\n";
     file << "  elseif(NOT EXISTS \"" << from_file << "\")\n"
     file << "  elseif(NOT EXISTS \"" << from_file << "\")\n"
@@ -2967,29 +2962,18 @@ void cmLocalGenerator::CopyPchCompilePdb(
          << " -E sleep 1)\n"
          << " -E sleep 1)\n"
          << "  endif()\n";
          << "  endif()\n";
     file << "endforeach()\n";
     file << "endforeach()\n";
+    outputs.push_back(configGenex(dest_file));
   }
   }
 
 
-  auto configGenex = [&](cm::string_view expr) -> std::string {
-    if (this->GetGlobalGenerator()->IsMultiConfig()) {
-      return cmStrCat("$<$<CONFIG:", config, ">:", expr, '>');
-    }
-    return std::string(expr);
-  };
-
-  cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
-    { configGenex(cmSystemTools::GetCMakeCommand()),
-      configGenex(cmStrCat("-DPDB_PREFIX=", pdb_prefix)), configGenex("-P"),
-      configGenex(copy_script) });
+  cmCustomCommandLines commandLines =
+    cmMakeSingleCommandLine({ configGenex(cmSystemTools::GetCMakeCommand()),
+                              configGenex("-P"), configGenex(copy_script) });
 
 
   auto const comment =
   auto const comment =
     cmStrCat("Copying PDB for PCH reuse from ", reuseTarget->GetName(),
     cmStrCat("Copying PDB for PCH reuse from ", reuseTarget->GetName(),
              " for ", target->GetName());
              " for ", target->GetName());
   ;
   ;
 
 
-  std::vector<std::string> outputs;
-  outputs.push_back(configGenex(
-    cmStrCat(target_compile_pdb_dir, pdb_prefix, ReuseFrom, ".pdb")));
-
   auto cc = cm::make_unique<cmCustomCommand>();
   auto cc = cm::make_unique<cmCustomCommand>();
   cc->SetCommandLines(commandLines);
   cc->SetCommandLines(commandLines);
   cc->SetComment(comment.c_str());
   cc->SetComment(comment.c_str());
@@ -3011,9 +2995,6 @@ void cmLocalGenerator::CopyPchCompilePdb(
       target->AddSource(copy_rule->ResolveFullPath());
       target->AddSource(copy_rule->ResolveFullPath());
     }
     }
   }
   }
-
-  target->Target->SetProperty("COMPILE_PDB_OUTPUT_DIRECTORY",
-                              target_compile_pdb_dir);
 }
 }
 
 
 cm::optional<std::string> cmLocalGenerator::GetMSVCDebugFormatName(
 cm::optional<std::string> cmLocalGenerator::GetMSVCDebugFormatName(

+ 0 - 1
Source/cmLocalGenerator.h

@@ -640,7 +640,6 @@ private:
   void CopyPchCompilePdb(std::string const& config,
   void CopyPchCompilePdb(std::string const& config,
                          std::string const& language,
                          std::string const& language,
                          cmGeneratorTarget* target,
                          cmGeneratorTarget* target,
-                         std::string const& ReuseFrom,
                          cmGeneratorTarget* reuseTarget,
                          cmGeneratorTarget* reuseTarget,
                          std::vector<std::string> const& extensions);
                          std::vector<std::string> const& extensions);
 
 

+ 3 - 17
Source/cmTarget.cxx

@@ -2148,24 +2148,10 @@ void cmTarget::SetProperty(std::string const& prop, cmValue value)
       return;
       return;
     }
     }
   } else if (prop == propPRECOMPILE_HEADERS_REUSE_FROM) {
   } else if (prop == propPRECOMPILE_HEADERS_REUSE_FROM) {
-    auto* reusedTarget = this->impl->Makefile->GetCMakeInstance()
-                           ->GetGlobalGenerator()
-                           ->FindTarget(value);
-    if (!reusedTarget) {
-      std::string const e(
-        "PRECOMPILE_HEADERS_REUSE_FROM set with non existing target");
-      this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
-      return;
-    }
-
-    std::string reusedFrom = reusedTarget->GetSafeProperty(prop);
-    if (reusedFrom.empty()) {
-      reusedFrom = *value;
+    this->impl->Properties.SetProperty(prop, value);
+    if (value) {
+      this->AddUtility(*value, false, this->impl->Makefile);
     }
     }
-
-    this->impl->Properties.SetProperty(prop, reusedFrom);
-
-    this->AddUtility(reusedFrom, false, this->impl->Makefile);
   } else if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
   } else if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
              prop == propCUDA_STANDARD || prop == propHIP_STANDARD ||
              prop == propCUDA_STANDARD || prop == propHIP_STANDARD ||
              prop == propOBJC_STANDARD || prop == propOBJCXX_STANDARD) {
              prop == propOBJC_STANDARD || prop == propOBJCXX_STANDARD) {

+ 12 - 1
Tests/PDBDirectoryAndName/CMakeLists.txt

@@ -76,16 +76,27 @@ endif()
 
 
 set(pdbs "")
 set(pdbs "")
 foreach(t ${my_targets})
 foreach(t ${my_targets})
+  set(with_compile 0)
   get_property(pdb_name TARGET ${t} PROPERTY PDB_NAME)
   get_property(pdb_name TARGET ${t} PROPERTY PDB_NAME)
   get_property(pdb_dir TARGET ${t} PROPERTY PDB_OUTPUT_DIRECTORY)
   get_property(pdb_dir TARGET ${t} PROPERTY PDB_OUTPUT_DIRECTORY)
   if(NOT pdb_name)
   if(NOT pdb_name)
+    set(with_compile 1)
     get_property(pdb_name TARGET ${t} PROPERTY COMPILE_PDB_NAME)
     get_property(pdb_name TARGET ${t} PROPERTY COMPILE_PDB_NAME)
   endif()
   endif()
   if(NOT pdb_dir)
   if(NOT pdb_dir)
     get_property(pdb_dir TARGET ${t} PROPERTY COMPILE_PDB_OUTPUT_DIRECTORY)
     get_property(pdb_dir TARGET ${t} PROPERTY COMPILE_PDB_OUTPUT_DIRECTORY)
   endif()
   endif()
   if(NOT pdb_dir)
   if(NOT pdb_dir)
-    set(pdb_dir ${CMAKE_CURRENT_BINARY_DIR})
+    if (NOT with_compile)
+      set(pdb_dir ${CMAKE_CURRENT_BINARY_DIR})
+    elseif (CMAKE_GENERATOR MATCHES "Ninja" OR
+            CMAKE_GENERATOR MATCHES "Makefiles")
+      set(pdb_dir ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${t}.dir)
+    elseif (CMAKE_GENERATOR MATCHES "Visual Studio")
+      set(pdb_dir ${CMAKE_CURRENT_BINARY_DIR}/${t}.dir)
+    else ()
+      set(pdb_dir ${CMAKE_CURRENT_BINARY_DIR}/${t}.dir)
+    endif ()
   endif()
   endif()
   if (pdb_dir MATCHES "\\$<.*>")
   if (pdb_dir MATCHES "\\$<.*>")
     # Skip per-configuration subdirectory if the value contained
     # Skip per-configuration subdirectory if the value contained

+ 2 - 0
Tests/RunCMake/PrecompileHeaders/PchReuseConsistency-build-stderr.txt

@@ -0,0 +1,2 @@
+((Classic Intel)?Warning #672: the command line options do not match those used[^
+]*)?

+ 12 - 0
Tests/RunCMake/PrecompileHeaders/PchReuseConsistency.cmake

@@ -54,12 +54,24 @@ file(WRITE ${CMAKE_BINARY_DIR}/message.hxx [=[
 const char* message();
 const char* message();
 ]=])
 ]=])
 
 
+add_library(pch_before_reuse_pch ${CMAKE_BINARY_DIR}/message.cxx)
+target_precompile_headers(pch_before_reuse_pch PRIVATE "${CMAKE_BINARY_DIR}/string.hxx")
+target_precompile_headers(pch_before_reuse_pch REUSE_FROM pch-generator)
+set_property(TARGET pch_before_reuse_pch PROPERTY PRECOMPILE_HEADERS_REUSE_FROM)
+target_include_directories(pch_before_reuse_pch PRIVATE ${CMAKE_BINARY_DIR})
+
 add_library(pch_before_reuse_reuse ${CMAKE_BINARY_DIR}/message.cxx)
 add_library(pch_before_reuse_reuse ${CMAKE_BINARY_DIR}/message.cxx)
 target_precompile_headers(pch_before_reuse_reuse PRIVATE "${CMAKE_BINARY_DIR}/string.hxx")
 target_precompile_headers(pch_before_reuse_reuse PRIVATE "${CMAKE_BINARY_DIR}/string.hxx")
 target_precompile_headers(pch_before_reuse_reuse REUSE_FROM pch-generator)
 target_precompile_headers(pch_before_reuse_reuse REUSE_FROM pch-generator)
 set_property(TARGET pch_before_reuse_reuse PROPERTY PRECOMPILE_HEADERS "")
 set_property(TARGET pch_before_reuse_reuse PROPERTY PRECOMPILE_HEADERS "")
 target_include_directories(pch_before_reuse_reuse PRIVATE ${CMAKE_BINARY_DIR})
 target_include_directories(pch_before_reuse_reuse PRIVATE ${CMAKE_BINARY_DIR})
 
 
+add_library(reuse_before_pch_pch ${CMAKE_BINARY_DIR}/message.cxx)
+target_precompile_headers(reuse_before_pch_pch REUSE_FROM pch-generator)
+target_precompile_headers(reuse_before_pch_pch PRIVATE "${CMAKE_BINARY_DIR}/string.hxx")
+set_property(TARGET reuse_before_pch_pch PROPERTY PRECOMPILE_HEADERS_REUSE_FROM)
+target_include_directories(reuse_before_pch_pch PRIVATE ${CMAKE_BINARY_DIR})
+
 add_library(reuse_before_pch_reuse ${CMAKE_BINARY_DIR}/message.cxx)
 add_library(reuse_before_pch_reuse ${CMAKE_BINARY_DIR}/message.cxx)
 target_precompile_headers(reuse_before_pch_reuse REUSE_FROM pch-generator)
 target_precompile_headers(reuse_before_pch_reuse REUSE_FROM pch-generator)
 target_precompile_headers(reuse_before_pch_reuse PRIVATE "${CMAKE_BINARY_DIR}/string.hxx")
 target_precompile_headers(reuse_before_pch_reuse PRIVATE "${CMAKE_BINARY_DIR}/string.hxx")

+ 2 - 0
Tests/RunCMake/PrecompileHeaders/PchReuseDeclarationOrder-build-stderr.txt

@@ -0,0 +1,2 @@
+((Classic Intel)?Warning #672: the command line options do not match those used[^
+]*)?

+ 59 - 0
Tests/RunCMake/PrecompileHeaders/PchReuseDeclarationOrder.cmake

@@ -0,0 +1,59 @@
+enable_language(CXX)
+
+if(CMAKE_CXX_COMPILE_OPTIONS_USE_PCH)
+  add_definitions(-DHAVE_PCH_SUPPORT)
+endif()
+
+######################################################################
+
+file(WRITE ${CMAKE_BINARY_DIR}/message.cxx [=[
+#include "message.hxx"
+
+#ifndef HAVE_PCH_SUPPORT
+  #include "string.hxx"
+#endif
+
+const char* message()
+{
+  static std::string greeting("hi there");
+  return greeting.c_str();
+}
+]=])
+
+file(WRITE ${CMAKE_BINARY_DIR}/message.hxx [=[
+const char* message();
+]=])
+
+add_library(reuse_decl_order ${CMAKE_BINARY_DIR}/message.cxx)
+target_precompile_headers(reuse_decl_order REUSE_FROM pch-generator)
+target_include_directories(reuse_decl_order PRIVATE ${CMAKE_BINARY_DIR})
+
+######################################################################
+
+file(WRITE ${CMAKE_BINARY_DIR}/pch.cxx [=[
+void nothing()
+{
+}
+]=])
+
+file(WRITE ${CMAKE_BINARY_DIR}/string.hxx [=[
+#include <string.h>
+
+namespace std {
+  struct string
+  {
+    char storage[20];
+
+    string(const char* s) {
+      strcpy(storage, s);
+    }
+
+    const char* c_str() const {
+      return storage;
+    }
+  };
+}
+]=])
+
+add_library(pch-generator ${CMAKE_BINARY_DIR}/pch.cxx)
+target_precompile_headers(pch-generator PRIVATE ${CMAKE_BINARY_DIR}/string.hxx)

+ 1 - 0
Tests/RunCMake/PrecompileHeaders/PchReuseFromCycle-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/PrecompileHeaders/PchReuseFromCycle-stderr.txt

@@ -0,0 +1,2 @@
+CMake Error in CMakeLists.txt:
+  Circular PCH reuse target involving 'first'

+ 10 - 0
Tests/RunCMake/PrecompileHeaders/PchReuseFromCycle.cmake

@@ -0,0 +1,10 @@
+enable_language(C)
+
+add_library(first foo.c)
+target_precompile_headers(first REUSE_FROM third)
+
+add_library(second foo.c)
+target_precompile_headers(second REUSE_FROM first)
+
+add_library(third foo.c)
+target_precompile_headers(third REUSE_FROM second)

+ 2 - 0
Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake

@@ -32,7 +32,9 @@ run_test(PchReuseFromSubdir)
 run_build_verbose(PchReuseFromIgnoreOwnProps)
 run_build_verbose(PchReuseFromIgnoreOwnProps)
 run_build_verbose(PchReuseFromUseUpdatedProps)
 run_build_verbose(PchReuseFromUseUpdatedProps)
 run_build_verbose(PchReuseConsistency)
 run_build_verbose(PchReuseConsistency)
+run_cmake(PchReuseFromCycle)
 run_cmake(PchMultilanguage)
 run_cmake(PchMultilanguage)
+run_build_verbose(PchReuseDeclarationOrder)
 if(RunCMake_GENERATOR MATCHES "Make|Ninja")
 if(RunCMake_GENERATOR MATCHES "Make|Ninja")
   run_cmake(PchWarnInvalid)
   run_cmake(PchWarnInvalid)