Browse Source

Merge topic 'autogen-moc-version'

5b0ea5874a AutoGen: Retrieve Qt version from moc as fallback

Acked-by: Kitware Robot <[email protected]>
Merge-request: !6027
Brad King 4 năm trước cách đây
mục cha
commit
e69a328725

+ 2 - 1
Source/cmQtAutoGenGlobalInitializer.cxx

@@ -119,7 +119,8 @@ cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer(
           target->GetSafeProperty(this->kw().AUTORCC_EXECUTABLE);
 
         // We support Qt4, Qt5 and Qt6
-        auto qtVersion = cmQtAutoGenInitializer::GetQtVersion(target.get());
+        auto qtVersion =
+          cmQtAutoGenInitializer::GetQtVersion(target.get(), mocExec);
         bool const validQt = (qtVersion.first.Major == 4) ||
           (qtVersion.first.Major == 5) || (qtVersion.first.Major == 6);
 

+ 72 - 2
Source/cmQtAutoGenInitializer.cxx

@@ -6,8 +6,8 @@
 #include <deque>
 #include <initializer_list>
 #include <map>
-#include <ostream>
 #include <set>
+#include <sstream> // for basic_ios, istringstream
 #include <string>
 #include <unordered_set>
 #include <utility>
@@ -1833,8 +1833,63 @@ void cmQtAutoGenInitializer::ConfigFileClean(ConfigString& configString)
   }
 }
 
+static cmQtAutoGen::IntegerVersion parseMocVersion(std::string str)
+{
+  cmQtAutoGen::IntegerVersion result;
+
+  static const std::string prelude = "moc ";
+  size_t pos = str.find(prelude);
+  if (pos == std::string::npos) {
+    return result;
+  }
+
+  str.erase(0, prelude.size() + pos);
+  std::istringstream iss(str);
+  std::string major;
+  std::string minor;
+  if (!std::getline(iss, major, '.') || !std::getline(iss, minor, '.')) {
+    return result;
+  }
+
+  result.Major = static_cast<unsigned int>(std::stoi(major));
+  result.Minor = static_cast<unsigned int>(std::stoi(minor));
+  return result;
+}
+
+static cmQtAutoGen::IntegerVersion GetMocVersion(
+  const std::string& mocExecutablePath)
+{
+  std::string capturedStdOut;
+  int exitCode;
+  if (!cmSystemTools::RunSingleCommand({ mocExecutablePath, "--version" },
+                                       &capturedStdOut, nullptr, &exitCode,
+                                       nullptr, cmSystemTools::OUTPUT_NONE)) {
+    return {};
+  }
+
+  if (exitCode != 0) {
+    return {};
+  }
+
+  return parseMocVersion(capturedStdOut);
+}
+
+static std::string FindMocExecutableFromMocTarget(cmMakefile* makefile,
+                                                  unsigned int qtMajorVersion)
+{
+  std::string result;
+  const std::string mocTargetName =
+    "Qt" + std::to_string(qtMajorVersion) + "::moc";
+  cmTarget* mocTarget = makefile->FindTargetToUse(mocTargetName);
+  if (mocTarget) {
+    result = mocTarget->GetSafeProperty("IMPORTED_LOCATION");
+  }
+  return result;
+}
+
 std::pair<cmQtAutoGen::IntegerVersion, unsigned int>
-cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target)
+cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target,
+                                     std::string mocExecutable)
 {
   // Converts a char ptr to an unsigned int value
   auto toUInt = [](const char* const input) -> unsigned int {
@@ -1909,6 +1964,21 @@ cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target)
       }
     }
   }
+
+  if (res.first.Major == 0) {
+    // We could not get the version number from variables or directory
+    // properties. This might happen if the find_package call for Qt is wrapped
+    // in a function. Try to find the moc executable path from the available
+    // targets and call "moc --version" to get the Qt version.
+    if (mocExecutable.empty()) {
+      mocExecutable =
+        FindMocExecutableFromMocTarget(target->Makefile, res.second);
+    }
+    if (!mocExecutable.empty()) {
+      res.first = GetMocVersion(mocExecutable);
+    }
+  }
+
   return res;
 }
 

+ 4 - 2
Source/cmQtAutoGenInitializer.h

@@ -98,9 +98,11 @@ public:
       , GenNameUpper(cmQtAutoGen::GeneratorNameUpper(gen)){};
   };
 
-  /** @return The detected Qt version and the required Qt major version.  */
+  /** @param mocExecutable The file path to the moc executable. Will be used as
+     fallback to query the version
+      @return The detected Qt version and the required Qt major version. */
   static std::pair<IntegerVersion, unsigned int> GetQtVersion(
-    cmGeneratorTarget const* genTarget);
+    cmGeneratorTarget const* genTarget, std::string mocExecutable);
 
   cmQtAutoGenInitializer(cmQtAutoGenGlobalInitializer* globalInitializer,
                          cmGeneratorTarget* genTarget,

+ 1 - 0
Tests/QtAutogen/Tests.cmake

@@ -49,6 +49,7 @@ if(QT_TEST_VERSION GREATER 4)
   ADD_AUTOGEN_TEST(MocMacroName mocMacroName)
   ADD_AUTOGEN_TEST(MocOsMacros)
   ADD_AUTOGEN_TEST(RerunMocPlugin)
+  ADD_AUTOGEN_TEST(WrappedFindPackage)
   if(APPLE)
     ADD_AUTOGEN_TEST(MacOsFW)
   endif()

+ 16 - 0
Tests/QtAutogen/WrappedFindPackage/CMakeLists.txt

@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.20)
+project(WrappedFindPackage)
+
+# Wrap the find_package call in a function.
+# Test whether AutoMoc can retrieve the Qt version from the moc executable.
+function(find_qt_package)
+  include("../AutogenCoreTest.cmake")
+  set(QT_LIBRARIES "${QT_LIBRARIES}" PARENT_SCOPE)
+endfunction()
+
+find_qt_package()
+
+set(CMAKE_AUTOMOC ON)
+
+add_executable(wrappedFindPackage main.cpp)
+target_link_libraries(wrappedFindPackage PRIVATE ${QT_LIBRARIES})

+ 19 - 0
Tests/QtAutogen/WrappedFindPackage/main.cpp

@@ -0,0 +1,19 @@
+#include <qobject.h>
+
+class MyObject : public QObject
+{
+  Q_OBJECT
+public:
+  MyObject(QObject* parent = 0)
+    : QObject(parent)
+  {
+  }
+};
+
+int main()
+{
+  MyObject obj;
+  return 0;
+}
+
+#include "main.moc"