summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--cmake/QtBuild.cmake15
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/corelib/.prev_CMakeLists.txt1
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/corelib/Qt6CoreMacros.cmake58
-rw-r--r--src/gui/.prev_CMakeLists.txt1
-rw-r--r--src/gui/CMakeLists.txt1
-rw-r--r--src/tools/cmake_automoc_parser/CMakeLists.txt18
-rw-r--r--src/tools/cmake_automoc_parser/main.cpp389
-rw-r--r--src/widgets/.prev_CMakeLists.txt1
-rw-r--r--src/widgets/CMakeLists.txt1
-rwxr-xr-xutil/cmake/pro2cmake.py2
13 files changed, 491 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2b92df6853..d18e91a6f4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,6 +56,9 @@ if(NOT QT_BUILD_STANDALONE_TESTS)
## feature variables are available.
qt_set_language_standards()
+ #include CoreMacros() for qt6_generate_meta_types()
+ include(src/corelib/Qt6CoreMacros.cmake)
+
## Visit all the directories:
add_subdirectory(src)
endif()
diff --git a/cmake/QtBuild.cmake b/cmake/QtBuild.cmake
index 6f325adfb4..cf4e56ca19 100644
--- a/cmake/QtBuild.cmake
+++ b/cmake/QtBuild.cmake
@@ -1110,6 +1110,7 @@ function(qt_extend_target target)
${private_visibility_option} ${arg_LINK_OPTIONS})
if(NOT arg_HEADER_MODULE)
+ list(APPEND arg_MOC_OPTIONS "--output-json")
set_target_properties("${target}" PROPERTIES
AUTOMOC_MOC_OPTIONS "${arg_MOC_OPTIONS}"
_qt_target_deps "${target_deps}"
@@ -1354,7 +1355,7 @@ function(qt_add_module target)
# Process arguments:
qt_parse_all_arguments(arg "qt_add_module"
- "NO_MODULE_HEADERS;STATIC;DISABLE_TOOLS_EXPORT;EXCEPTIONS;INTERNAL_MODULE;NO_SYNC_QT;NO_PRIVATE_MODULE;HEADER_MODULE"
+ "NO_MODULE_HEADERS;STATIC;DISABLE_TOOLS_EXPORT;EXCEPTIONS;INTERNAL_MODULE;NO_SYNC_QT;NO_PRIVATE_MODULE;HEADER_MODULE;GENERATE_METATYPES"
"CONFIG_MODULE_NAME;PRECOMPILED_HEADER"
"${__default_private_args};${__default_public_args};QMAKE_MODULE_CONFIG;EXTRA_CMAKE_FILES;EXTRA_CMAKE_INCLUDES;NO_PCH_SOURCES" ${ARGN})
@@ -1696,6 +1697,18 @@ set(QT_CMAKE_EXPORT_NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE})")
endif()
qt_describe_module(${target})
+
+ # Generate metatypes
+ if (${arg_GENERATE_METATYPES})
+ qt6_generate_meta_types_json_file(${target})
+ get_target_property(target_metatypes_file ${target} QT_MODULE_META_TYPES_FILE)
+ if (target_metatypes_file)
+ set(metatypes_install_dir ${INSTALL_LIBDIR}/metatypes)
+ qt_copy_or_install(FILES ${target_metatypes_file}
+ DESTINATION ${metatypes_install_dir}
+ )
+ endif()
+ endif()
endfunction()
function(qt_export_tools module_name)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e7a8255029..bb2b8a6ee2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -8,6 +8,7 @@ function(find_or_build_bootstrap_names)
add_subdirectory(tools/moc)
add_subdirectory(tools/rcc)
add_subdirectory(tools/tracegen)
+ add_subdirectory(tools/cmake_automoc_parser)
endfunction()
find_or_build_bootstrap_names()
diff --git a/src/corelib/.prev_CMakeLists.txt b/src/corelib/.prev_CMakeLists.txt
index 4ccfbd92b6..3a43b4f03c 100644
--- a/src/corelib/.prev_CMakeLists.txt
+++ b/src/corelib/.prev_CMakeLists.txt
@@ -5,6 +5,7 @@
#####################################################################
qt_add_module(Core
+ GENERATE_METATYPES
QMAKE_MODULE_CONFIG moc resources
EXCEPTIONS
SOURCES
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index 1c3803f7c5..f8d70de115 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -27,6 +27,7 @@ file(RELATIVE_PATH QT_INVERSE_CONFIG_INSTALL_DIR ${_clean_prefix} ${CMAKE_INSTAL
#####################################################################
qt_add_module(Core
+ GENERATE_METATYPES
QMAKE_MODULE_CONFIG moc resources
EXCEPTIONS
SOURCES
diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake
index dbc9cdb163..3da011ecb1 100644
--- a/src/corelib/Qt6CoreMacros.cmake
+++ b/src/corelib/Qt6CoreMacros.cmake
@@ -530,3 +530,61 @@ function(qt6_import_plugins TARGET_NAME)
endif()
endforeach()
endfunction()
+
+
+function(qt6_generate_meta_types_json_file target)
+
+ get_target_property(target_type ${target} TYPE)
+ if (target_type STREQUAL "INTERFACE_LIBRARY" OR CMAKE_VERSION VERSION_LESS "3.16.0")
+ # interface libraries not supported or cmake version is not high enough
+ message(WARNING "Meta types generation requires CMake >= 3.16")
+ return()
+ endif()
+
+ get_target_property(existing_meta_types_file ${target} QT_MODULE_META_TYPES_FILE)
+ if (existing_meta_types_file)
+ return()
+ endif()
+
+ get_target_property(target_binary_dir ${target} BINARY_DIR)
+
+ set(cmake_autogen_cache_file
+ "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/ParseCache.txt")
+ set(cmake_autogen_info_file
+ "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/AutogenInfo.json")
+ set(type_list_file "${target_binary_dir}/meta_types/json_file_list.txt")
+
+ add_custom_target(${target}_automoc_json_extraction
+ BYPRODUCTS ${type_list_file}
+ COMMAND
+ ${QT_CMAKE_EXPORT_NAMESPACE}::cmake_automoc_parser
+ --cmake-autogen-cache-file "${cmake_autogen_cache_file}"
+ --cmake-autogen-info-file "${cmake_autogen_info_file}"
+ --output-file-path "${type_list_file}"
+ --cmake-autogen-include-dir-path "${target_binary_dir}/${target}_autogen/include"
+ COMMENT "Running Automoc file extraction"
+ )
+
+ add_dependencies(${target}_automoc_json_extraction ${target}_autogen)
+
+ if (CMAKE_BUILD_TYPE)
+ string(TOLOWER ${target}_${CMAKE_BUILD_TYPE} target_lowercase)
+ else()
+ message(FATAL_ERROR "add_custom_command's OUTPUT parameter does not support generator expressions, so we can't generate this file for multi-config generators")
+ endif()
+ set(metatypes_file "${target_binary_dir}/meta_types/qt6${target_lowercase}_metatypes.json")
+ add_custom_command(OUTPUT ${metatypes_file}
+ DEPENDS ${type_list_file}
+ COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::moc
+ -o ${metatypes_file}
+ --collect-json "@${type_list_file}"
+ COMMENT "Runing automoc with --collect-json"
+ )
+
+ target_sources(${target} PRIVATE ${metatypes_file})
+ set_source_files_properties(${metatypes_file} PROPERTIES HEADER_FILE_ONLY TRUE)
+
+ set_target_properties(${target} PROPERTIES
+ QT_MODULE_META_TYPES_FILE ${metatypes_file}
+ )
+endfunction()
diff --git a/src/gui/.prev_CMakeLists.txt b/src/gui/.prev_CMakeLists.txt
index 68cf6aab0c..90b66fd882 100644
--- a/src/gui/.prev_CMakeLists.txt
+++ b/src/gui/.prev_CMakeLists.txt
@@ -5,6 +5,7 @@
#####################################################################
qt_add_module(Gui
+ GENERATE_METATYPES
PLUGIN_TYPES platforms platforms/darwin xcbglintegrations platformthemes platforminputcontexts generic iconengines imageformats egldeviceintegrations
SOURCES
image/qbitmap.cpp image/qbitmap.h
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 9f146721c0..e14cbc4144 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -46,6 +46,7 @@ endif()
# special case end
qt_add_module(Gui
+ GENERATE_METATYPES
PLUGIN_TYPES platforms platforms/darwin xcbglintegrations platformthemes platforminputcontexts generic iconengines imageformats egldeviceintegrations
FEATURE_DEPENDENCIES # special case:
Qt::Network # special case:
diff --git a/src/tools/cmake_automoc_parser/CMakeLists.txt b/src/tools/cmake_automoc_parser/CMakeLists.txt
new file mode 100644
index 0000000000..73303215bb
--- /dev/null
+++ b/src/tools/cmake_automoc_parser/CMakeLists.txt
@@ -0,0 +1,18 @@
+#####################################################################
+## moc Tool:
+#####################################################################
+
+qt_add_tool(cmake_automoc_parser
+ BOOTSTRAP
+ TOOLS_TARGET Core # special case
+ SOURCES
+ main.cpp
+ DEFINES
+ QT_MOC
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_FROM_BYTEARRAY
+ QT_NO_COMPRESS
+ QT_NO_FOREACH
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+)
diff --git a/src/tools/cmake_automoc_parser/main.cpp b/src/tools/cmake_automoc_parser/main.cpp
new file mode 100644
index 0000000000..456b06b072
--- /dev/null
+++ b/src/tools/cmake_automoc_parser/main.cpp
@@ -0,0 +1,389 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qglobal.h>
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <qfile.h>
+#include <qjsonarray.h>
+#include <qjsondocument.h>
+#include <qjsonobject.h>
+#include <qdir.h>
+#include <qstring.h>
+#include <qhash.h>
+#include <qvector.h>
+#include <qstack.h>
+#include <qdebug.h>
+#include <qset.h>
+#include <qmap.h>
+#include <qcoreapplication.h>
+#include <qcommandlineoption.h>
+#include <qcommandlineparser.h>
+
+QT_BEGIN_NAMESPACE
+
+using AutoGenHeaderMap = QMap<QString, QString>;
+using AutoGenSourcesList = QVector<QString>;
+
+static bool readAutogenInfoJson(AutoGenHeaderMap &headers, AutoGenSourcesList &sources,
+ QStringList &headerExts, const QString &autoGenInfoJsonPath)
+{
+ QFile file(autoGenInfoJsonPath);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ fprintf(stderr, "Could not open: %s\n", qPrintable(autoGenInfoJsonPath));
+ return false;
+ }
+
+ const QByteArray contents = file.readAll();
+ file.close();
+
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(contents, &error);
+
+ if (error.error != QJsonParseError::NoError) {
+ fprintf(stderr, "Failed to parse json file: %s\n", qPrintable(autoGenInfoJsonPath));
+ return false;
+ }
+
+ QJsonObject rootObject = doc.object();
+ QJsonValue headersValue = rootObject.value(QLatin1String("HEADERS"));
+ QJsonValue sourcesValue = rootObject.value(QLatin1String("SOURCES"));
+ QJsonValue headerExtValue = rootObject.value(QLatin1String("HEADER_EXTENSIONS"));
+
+ if (!headersValue.isArray() || !sourcesValue.isArray() || !headerExtValue.isArray()) {
+ fprintf(stderr,
+ "%s layout does not match the expected layout. This most likely means that file "
+ "format changed or this file is not a product of CMake's AutoGen process.\n",
+ qPrintable(autoGenInfoJsonPath));
+ return false;
+ }
+
+ QJsonArray headersArray = headersValue.toArray();
+ QJsonArray sourcesArray = sourcesValue.toArray();
+ QJsonArray headerExtArray = headerExtValue.toArray();
+
+ for (const auto &value : headersArray) {
+ QJsonArray entry_array = value.toArray();
+ if (entry_array.size() > 2) {
+ // Array[0] : header path
+ // Array[2] : Location of the generated moc file for this header
+ // if no source file includes it
+ headers.insert(entry_array[0].toString(), entry_array[2].toString());
+ }
+ }
+
+ sources.reserve(sourcesArray.size());
+ for (const auto &value : sourcesArray) {
+ QJsonArray entry_array = value.toArray();
+ if (entry_array.size() > 1) {
+ sources.push_back(entry_array[0].toString());
+ }
+ }
+
+ headerExts.reserve(headerExtArray.size());
+ for (const auto &value : headerExtArray) {
+ headerExts.push_back(value.toString());
+ }
+
+ return true;
+}
+
+struct ParseCacheEntry
+{
+ QStringList mocFiles;
+ QStringList mocIncludes;
+};
+
+using ParseCacheMap = QMap<QString, ParseCacheEntry>;
+
+static bool readParseCache(ParseCacheMap &entries, const QString &parseCacheFilePath)
+{
+
+ QFile file(parseCacheFilePath);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ fprintf(stderr, "Could not open: %s\n", qPrintable(parseCacheFilePath));
+ return false;
+ }
+
+ QString source;
+ QStringList mocEntries;
+ QStringList mocIncludes;
+
+ // File format
+ // ....
+ // header/source path N
+ // mmc:Q_OBJECT| mcc:Q_GADGET # This file has been mocked
+ // miu:moc_....cpp # Path of the moc.cpp file generated for the above file
+ // relative to TARGET_BINARY_DIR/TARGET_autgen/include directory. Not
+ // present for headers.
+ // mid: ....moc # Path of .moc file generated for the above file relative
+ // to TARGET_BINARY_DIR/TARGET_autogen/include directory.
+ // uic: UI related info, ignored
+ // mdp: Moc dependencies, ignored
+ // udp: UI dependencies, ignored
+ // header/source path N + 1
+ // ....
+
+ QTextStream textStream(&file);
+ const QString mmcKey = QString(QLatin1String(" mmc:"));
+ const QString miuKey = QString(QLatin1String(" miu:"));
+ const QString uicKey = QString(QLatin1String(" uic:"));
+ const QString midKey = QString(QLatin1String(" mid:"));
+ const QString mdpKey = QString(QLatin1String(" mdp:"));
+ const QString udpKey = QString(QLatin1String(" udp:"));
+ QString line;
+ bool mmc_key_found = false;
+ while (textStream.readLineInto(&line)) {
+ if (!line.startsWith(QLatin1Char(' '))) {
+ if (!mocEntries.isEmpty() || mmc_key_found || !mocIncludes.isEmpty()) {
+ entries.insert(source,
+ ParseCacheEntry { std::move(mocEntries), std::move(mocIncludes) });
+ source.clear();
+ mmc_key_found = false;
+ }
+ source = line;
+ } else if (line.startsWith(mmcKey)) {
+ mmc_key_found = true;
+ } else if (line.startsWith(miuKey)) {
+ mocIncludes.push_back(line.right(line.size() - miuKey.size()));
+ } else if (line.startsWith(midKey)) {
+ mocEntries.push_back(line.right(line.size() - midKey.size()));
+ } else if (line.startsWith(uicKey) || line.startsWith(mdpKey) || line.startsWith(udpKey)) {
+ // nothing to do ignore
+ continue;
+ } else {
+ fprintf(stderr, "Unhandled line entry \"%s\" in %s\n", qPrintable(line),
+ qPrintable(parseCacheFilePath));
+ return false;
+ }
+ }
+
+ // Check if last entry has any data left to processed
+ if (!mocEntries.isEmpty() || !mocIncludes.isEmpty() || mmc_key_found) {
+ entries.insert(source, ParseCacheEntry { std::move(mocEntries), std::move(mocIncludes) });
+ }
+
+ file.close();
+ return true;
+}
+
+static bool readJsonFiles(QVector<QString> &entries, const QString &filePath)
+{
+
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ fprintf(stderr, "Could not open: %s\n", qPrintable(filePath));
+ return false;
+ }
+
+ QTextStream textStream(&file);
+ QString line;
+ while (textStream.readLineInto(&line)) {
+ entries.push_back(line);
+ }
+ file.close();
+ return true;
+}
+
+static bool writeJsonFiles(const QVector<QString> &fileList, const QString &fileListFilePath)
+{
+ QFile file(fileListFilePath);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ fprintf(stderr, "Could not open: %s\n", qPrintable(fileListFilePath));
+ return false;
+ }
+
+ QTextStream textStream(&file);
+ for (const auto &file : fileList) {
+ textStream << file << Qt::endl;
+ }
+
+ file.close();
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+
+ QCoreApplication app(argc, argv);
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QStringLiteral("Qt CMake Autogen parser tool"));
+
+ parser.addHelpOption();
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+
+ QCommandLineOption outputFileOption(QStringLiteral("output-file-path"));
+ outputFileOption.setDescription(
+ QStringLiteral("Output file where the meta type file list will be written."));
+ outputFileOption.setValueName(QStringLiteral("output file"));
+ parser.addOption(outputFileOption);
+
+ QCommandLineOption cmakeAutogenCacheFileOption(QStringLiteral("cmake-autogen-cache-file"));
+ cmakeAutogenCacheFileOption.setDescription(
+ QStringLiteral("Location of the CMake AutoGen ParseCache.txt file."));
+ cmakeAutogenCacheFileOption.setValueName(QStringLiteral("CMake AutoGen ParseCache.txt file"));
+ parser.addOption(cmakeAutogenCacheFileOption);
+
+ QCommandLineOption cmakeAutogenInfoFileOption(QStringLiteral("cmake-autogen-info-file"));
+ cmakeAutogenInfoFileOption.setDescription(
+ QStringLiteral("Location of the CMake AutoGen AutogenInfo.json file."));
+ cmakeAutogenInfoFileOption.setValueName(QStringLiteral("CMake AutoGen AutogenInfo.json file"));
+ parser.addOption(cmakeAutogenInfoFileOption);
+
+ QCommandLineOption cmakeAutogenIncludeDirOption(
+ QStringLiteral("cmake-autogen-include-dir-path"));
+ cmakeAutogenIncludeDirOption.setDescription(
+ QStringLiteral("Location of the CMake AutoGen include directory."));
+ cmakeAutogenIncludeDirOption.setValueName(QStringLiteral("CMake AutoGen include directory"));
+ parser.addOption(cmakeAutogenIncludeDirOption);
+
+ QStringList arguments = QCoreApplication::arguments();
+ parser.process(arguments);
+
+ if (!parser.isSet(outputFileOption) || !parser.isSet(cmakeAutogenInfoFileOption)
+ || !parser.isSet(cmakeAutogenCacheFileOption)
+ || !parser.isSet(cmakeAutogenIncludeDirOption)) {
+ parser.showHelp(1);
+ return EXIT_FAILURE;
+ }
+
+ // Read source files from AutogenInfo.json
+ AutoGenHeaderMap autoGenHeaders;
+ AutoGenSourcesList autoGenSources;
+ QStringList headerExtList;
+ if (!readAutogenInfoJson(autoGenHeaders, autoGenSources, headerExtList,
+ parser.value(cmakeAutogenInfoFileOption))) {
+ return EXIT_FAILURE;
+ }
+
+ ParseCacheMap parseCacheEntries;
+ if (!readParseCache(parseCacheEntries, parser.value(cmakeAutogenCacheFileOption))) {
+ return EXIT_FAILURE;
+ }
+
+ const QString cmakeIncludeDir = parser.value(cmakeAutogenIncludeDirOption);
+
+ // Algorithm description
+ // 1) For each source from the AutoGenSources list check if there is a parse
+ // cache entry.
+ // 1a) If an entry was wound there exists an moc_...cpp file somewhere.
+ // Remove the header file from the AutoGenHeader files
+ // 1b) For every matched source entry, check the moc includes as it is
+ // possible for a source file to include moc files from other headers.
+ // Remove the header from AutoGenHeaders
+ // 2) For every remaining header in AutoGenHeaders, check if there is an
+ // entry for it in the parse cache. Use the value for the location of the
+ // moc.json file
+
+ QVector<QString> jsonFileList;
+ QDir dir(cmakeIncludeDir);
+ jsonFileList.reserve(autoGenSources.size());
+
+ // 1) Process sources
+ for (const auto &source : autoGenSources) {
+ auto it = parseCacheEntries.find(source);
+ if (it == parseCacheEntries.end()) {
+ continue;
+ }
+
+ const QFileInfo fileInfo(source);
+ const QString base = fileInfo.path() + fileInfo.completeBaseName();
+ // 1a) erase header
+ for (const auto &ext : headerExtList) {
+ const QString headerPath = base + QLatin1Char('.') + ext;
+ auto it = autoGenHeaders.find(headerPath);
+ if (it != autoGenHeaders.end()) {
+ autoGenHeaders.erase(it);
+ break;
+ }
+ }
+ // Add extra moc files
+ for (const auto &mocFile : it.value().mocFiles) {
+ jsonFileList.push_back(dir.filePath(mocFile) + QLatin1String(".json"));
+ }
+ // Add main moc files
+ for (const auto &mocFile : it.value().mocIncludes) {
+ jsonFileList.push_back(dir.filePath(mocFile) + QLatin1String(".json"));
+ // 1b) Locate this header and delete it
+ constexpr int mocKeyLen = 4; // length of "moc_"
+ const QString headerBaseName =
+ QFileInfo(mocFile.right(mocFile.size() - mocKeyLen)).completeBaseName();
+ bool breakFree = false;
+ for (auto &ext : headerExtList) {
+ const QString headerSuffix = headerBaseName + QLatin1Char('.') + ext;
+ for (auto it = autoGenHeaders.begin(); it != autoGenHeaders.end(); ++it) {
+ if (it.key().endsWith(headerSuffix)
+ && QFileInfo(it.key()).completeBaseName() == headerBaseName) {
+ autoGenHeaders.erase(it);
+ breakFree = true;
+ break;
+ }
+ }
+ if (breakFree) {
+ break;
+ }
+ }
+ }
+ }
+
+ // 2) Process headers
+ for (auto mapIt = autoGenHeaders.begin(); mapIt != autoGenHeaders.end(); ++mapIt) {
+ auto it = parseCacheEntries.find(mapIt.key());
+ if (it == parseCacheEntries.end()) {
+ continue;
+ }
+ const QString jsonPath =
+ dir.filePath(QLatin1String("../") + mapIt.value() + QLatin1String(".json"));
+ jsonFileList.push_back(jsonPath);
+ }
+
+ // Sort for consistent checks across runs
+ jsonFileList.sort();
+
+ // Read Previous file list (if any)
+ const QString fileListFilePath = parser.value(outputFileOption);
+ QVector<QString> previousList;
+ QFile prev_file(fileListFilePath);
+
+ // Only try to open file if it exists to avoid error messages
+ if (prev_file.exists()) {
+ (void)readJsonFiles(previousList, fileListFilePath);
+ }
+
+ if (previousList != jsonFileList || !QFile(fileListFilePath).exists()) {
+ if (!writeJsonFiles(jsonFileList, fileListFilePath)) {
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+QT_END_NAMESPACE
diff --git a/src/widgets/.prev_CMakeLists.txt b/src/widgets/.prev_CMakeLists.txt
index b920d0761e..c53bb3928f 100644
--- a/src/widgets/.prev_CMakeLists.txt
+++ b/src/widgets/.prev_CMakeLists.txt
@@ -5,6 +5,7 @@
#####################################################################
qt_add_module(Widgets
+ GENERATE_METATYPES
QMAKE_MODULE_CONFIG uic
PLUGIN_TYPES styles
SOURCES
diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt
index b65c2ddd45..35c6c60ae6 100644
--- a/src/widgets/CMakeLists.txt
+++ b/src/widgets/CMakeLists.txt
@@ -5,6 +5,7 @@
#####################################################################
qt_add_module(Widgets
+ GENERATE_METATYPES
QMAKE_MODULE_CONFIG uic
PLUGIN_TYPES styles
SOURCES
diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py
index 89d30a469b..7c54e2be2c 100755
--- a/util/cmake/pro2cmake.py
+++ b/util/cmake/pro2cmake.py
@@ -2827,6 +2827,8 @@ def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str:
extra.append("NO_PRIVATE_MODULE")
if "header_module" in scope.get("CONFIG"):
extra.append("HEADER_MODULE")
+ if "metatypes" in scope.get("CONFIG"):
+ extra.append("GENERATE_METATYPES")
module_config = scope.get("MODULE_CONFIG")
if len(module_config):