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
This commit is contained in:
Joerg Bornemann 2021-04-20 21:17:50 +02:00
parent 63ffe21036
commit 5b0ea5874a
6 changed files with 114 additions and 5 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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()

View File

@ -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})

View File

@ -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"