Merge branch 'autogen_cache_binary_checks' into release-3.14

Merge-request: !2990
diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx
index 95a297c..3ad91ee 100644
--- a/Source/cmQtAutoGenGlobalInitializer.cxx
+++ b/Source/cmQtAutoGenGlobalInitializer.cxx
@@ -6,10 +6,12 @@
 
 #include "cmAlgorithms.h"
 #include "cmCustomCommandLines.h"
+#include "cmDuration.h"
 #include "cmGeneratorTarget.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmProcessOutput.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
@@ -183,6 +185,68 @@
   }
 }
 
+bool cmQtAutoGenGlobalInitializer::GetExecutableTestOutput(
+  std::string const& generator, std::string const& executable,
+  std::string& error, std::string* output)
+{
+  // Check if we have cached output
+  {
+    auto it = this->ExecutableTestOutputs_.find(executable);
+    if (it != this->ExecutableTestOutputs_.end()) {
+      // Return output on demand
+      if (output != nullptr) {
+        *output = it->second;
+      }
+      return true;
+    }
+  }
+
+  // Check if the executable exists
+  if (!cmSystemTools::FileExists(executable, true)) {
+    error = "The \"";
+    error += generator;
+    error += "\" executable ";
+    error += cmQtAutoGen::Quoted(executable);
+    error += " does not exist.";
+    return false;
+  }
+
+  // Test the executable
+  std::string stdOut;
+  {
+    std::string stdErr;
+    std::vector<std::string> command;
+    command.push_back(executable);
+    command.emplace_back("-h");
+    int retVal = 0;
+    const bool runResult = cmSystemTools::RunSingleCommand(
+      command, &stdOut, &stdErr, &retVal, nullptr, cmSystemTools::OUTPUT_NONE,
+      cmDuration::zero(), cmProcessOutput::Auto);
+    if (!runResult) {
+      error = "Test run of \"";
+      error += generator;
+      error += "\" executable ";
+      error += cmQtAutoGen::Quoted(executable) + " failed.\n";
+      error += cmQtAutoGen::QuotedCommand(command);
+      error += "\n";
+      error += stdOut;
+      error += "\n";
+      error += stdErr;
+      return false;
+    }
+  }
+
+  // Return executable output on demand
+  if (output != nullptr) {
+    *output = stdOut;
+  }
+
+  // Register executable and output
+  this->ExecutableTestOutputs_.emplace(executable, std::move(stdOut));
+
+  return true;
+}
+
 bool cmQtAutoGenGlobalInitializer::generate()
 {
   return (InitializeCustomTargets() && SetupCustomTargets());
diff --git a/Source/cmQtAutoGenGlobalInitializer.h b/Source/cmQtAutoGenGlobalInitializer.h
index 9e6bac0..74184a0 100644
--- a/Source/cmQtAutoGenGlobalInitializer.h
+++ b/Source/cmQtAutoGenGlobalInitializer.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory> // IWYU pragma: keep
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 class cmLocalGenerator;
@@ -38,10 +39,15 @@
   void AddToGlobalAutoRcc(cmLocalGenerator* localGen,
                           std::string const& targetName);
 
+  bool GetExecutableTestOutput(std::string const& generator,
+                               std::string const& executable,
+                               std::string& error, std::string* output);
+
 private:
   std::vector<std::unique_ptr<cmQtAutoGenInitializer>> Initializers_;
   std::map<cmLocalGenerator*, std::string> GlobalAutoGenTargets_;
   std::map<cmLocalGenerator*, std::string> GlobalAutoRccTargets_;
+  std::unordered_map<std::string, std::string> ExecutableTestOutputs_;
 };
 
 #endif
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index a96d574..614a88b 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -1439,18 +1439,18 @@
   return res;
 }
 
-std::pair<bool, std::string> GetQtExecutable(
-  const cmQtAutoGen::IntegerVersion& qtVersion, cmGeneratorTarget* target,
+std::pair<bool, std::string> cmQtAutoGenInitializer::GetQtExecutable(
   const std::string& executable, bool ignoreMissingTarget, std::string* output)
 {
   const std::string upperExecutable = cmSystemTools::UpperCase(executable);
-  std::string result =
-    target->Target->GetSafeProperty("AUTO" + upperExecutable + "_EXECUTABLE");
+  std::string result = this->Target->Target->GetSafeProperty(
+    "AUTO" + upperExecutable + "_EXECUTABLE");
   if (!result.empty()) {
-    cmListFileBacktrace lfbt = target->Target->GetMakefile()->GetBacktrace();
+    cmListFileBacktrace lfbt =
+      this->Target->Target->GetMakefile()->GetBacktrace();
     cmGeneratorExpression ge(lfbt);
     std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(result);
-    result = cge->Evaluate(target->GetLocalGenerator(), "");
+    result = cge->Evaluate(this->Target->GetLocalGenerator(), "");
 
     return std::make_pair(true, result);
   }
@@ -1460,12 +1460,12 @@
   // Find executable
   {
     const std::string targetName =
-      GetQtExecutableTargetName(qtVersion, executable);
+      GetQtExecutableTargetName(this->QtVersion, executable);
     if (targetName.empty()) {
       err = "The AUTO" + upperExecutable + " feature ";
       err += "supports only Qt 4, Qt 5 and Qt 6.";
     } else {
-      cmLocalGenerator* localGen = target->GetLocalGenerator();
+      cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
       cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse(targetName);
       if (tgt != nullptr) {
         if (tgt->IsImported()) {
@@ -1485,36 +1485,14 @@
 
   // Test executable
   if (err.empty()) {
-    if (cmSystemTools::FileExists(result, true)) {
-      std::vector<std::string> command;
-      command.push_back(result);
-      command.emplace_back("-h");
-      std::string stdOut;
-      std::string stdErr;
-      int retVal = 0;
-      const bool runResult = cmSystemTools::RunSingleCommand(
-        command, &stdOut, &stdErr, &retVal, nullptr,
-        cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
-      if (!runResult) {
-        err = "Test of \"" + executable + "\" binary ";
-        err += cmQtAutoGen::Quoted(result) + " failed: ";
-        err += cmQtAutoGen::QuotedCommand(command);
-      } else {
-        if (output != nullptr) {
-          *output = stdOut;
-        }
-      }
-    } else {
-      err = "The \"" + executable + "\" binary ";
-      err += cmQtAutoGen::Quoted(result);
-      err += " does not exist";
-    }
+    this->GlobalInitializer->GetExecutableTestOutput(executable, result, err,
+                                                     output);
   }
 
   // Print error
   if (!err.empty()) {
     std::string msg = "AutoGen (";
-    msg += target->GetName();
+    msg += this->Target->GetName();
     msg += "): ";
     msg += err;
     cmSystemTools::Error(msg);
@@ -1526,16 +1504,14 @@
 
 bool cmQtAutoGenInitializer::GetMocExecutable()
 {
-  const auto result =
-    GetQtExecutable(this->QtVersion, this->Target, "moc", false, nullptr);
+  const auto result = this->GetQtExecutable("moc", false, nullptr);
   this->Moc.Executable = result.second;
   return result.first;
 }
 
 bool cmQtAutoGenInitializer::GetUicExecutable()
 {
-  const auto result =
-    GetQtExecutable(this->QtVersion, this->Target, "uic", true, nullptr);
+  const auto result = this->GetQtExecutable("uic", true, nullptr);
   this->Uic.Executable = result.second;
   return result.first;
 }
@@ -1543,8 +1519,7 @@
 bool cmQtAutoGenInitializer::GetRccExecutable()
 {
   std::string stdOut;
-  const auto result =
-    GetQtExecutable(this->QtVersion, this->Target, "rcc", false, &stdOut);
+  const auto result = this->GetQtExecutable("rcc", false, &stdOut);
   this->Rcc.Executable = result.second;
   if (!result.first) {
     return false;
diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h
index 10f0bf3..781dd15 100644
--- a/Source/cmQtAutoGenInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -110,6 +110,10 @@
                      std::vector<std::string>& files,
                      std::string& errorMessage);
 
+  std::pair<bool, std::string> GetQtExecutable(const std::string& executable,
+                                               bool ignoreMissingTarget,
+                                               std::string* output);
+
 private:
   cmQtAutoGenGlobalInitializer* GlobalInitializer;
   cmGeneratorTarget* Target;