summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeander Beernaert <leander.beernaert@qt.io>2019-11-26 10:10:55 +0100
committerLeander Beernaert <leander.beernaert@qt.io>2019-12-04 10:48:06 +0000
commit0f220d473a6048c0f1267a96ed5cc18fa39ee61c (patch)
tree20b4c0bbaa908e86973466e5b2b8ae8e52dbbca7
parent9eea24ed601d0aa243269fac64c9a65c3750d1b0 (diff)
Collect Json Metatypes from CMake's Automoc
This patch adds a new bootstrap tool which will read CMake's AutoGenInfo.json and ParseCache.txt to determine what the current list of json files is that needs to be passed to moc --collect-json option. Right now this is enabled for qt_add_module() with the option GENERATE_METATYPES. pro2cmake has also been updated to detect qmake's CONFIG += metatypes and to generate the above option for modules. The implementation lives in Qt6CoreMacros so it can eventually be used in the public facing apis. The generated meta types file is saved under the target property QT_MODULE_META_TYPES_FILE. Change-Id: I03709c662be81dd0912d0068c23ee2507bfe4383 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
-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):