Commit 5b0ea587 authored by Jörg Bornemann's avatar Jörg Bornemann
Browse files

AutoGen: Retrieve Qt version from moc as fallback

Consider the case where the find_package call for QtCore is wrapped in a
function call. Then AutoGen cannot determine the Qt version, because it
only looks at variables and directory properties. The former don't leave
the function scope and the latter are not set by default.

As a fallback, locate the moc executable via its target and call it with
the --version argument to determine the Qt version.

Issue: #22028
parent 63ffe210
......@@ -114,7 +114,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);
......
......@@ -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;
}
......
......@@ -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,
......
......@@ -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()
......
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})
#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"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment