diff options
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | cmake/QtBuild.cmake | 15 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/corelib/.prev_CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/corelib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/corelib/Qt6CoreMacros.cmake | 58 | ||||
-rw-r--r-- | src/gui/.prev_CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/gui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/tools/cmake_automoc_parser/CMakeLists.txt | 18 | ||||
-rw-r--r-- | src/tools/cmake_automoc_parser/main.cpp | 389 | ||||
-rw-r--r-- | src/widgets/.prev_CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/widgets/CMakeLists.txt | 1 | ||||
-rwxr-xr-x | util/cmake/pro2cmake.py | 2 |
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): |