diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/qmlcachegen/generateloader.cpp | 6 | ||||
-rw-r--r-- | tools/qmlcachegen/qmlcachegen.cpp | 6 | ||||
-rw-r--r-- | tools/qmlcachegen/qtquickcompiler.prf | 10 | ||||
-rw-r--r-- | tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in | 184 | ||||
-rw-r--r-- | tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in | 5 | ||||
-rw-r--r-- | tools/qmlimportscanner/main.cpp | 46 | ||||
-rw-r--r-- | tools/qmlimportscanner/qmlimportscanner.pro | 45 | ||||
-rw-r--r-- | tools/qmllint/findunqualified.cpp | 14 | ||||
-rw-r--r-- | tools/qmllint/findunqualified.h | 1 | ||||
-rw-r--r-- | tools/qmlplugindump/main.cpp | 133 |
10 files changed, 372 insertions, 78 deletions
diff --git a/tools/qmlcachegen/generateloader.cpp b/tools/qmlcachegen/generateloader.cpp index 74208a25e2..1c8a5a016a 100644 --- a/tools/qmlcachegen/generateloader.cpp +++ b/tools/qmlcachegen/generateloader.cpp @@ -434,7 +434,11 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR } } +#if QT_CONFIG(temporaryfile) QSaveFile f(outputFileName); +#else + QFile f(outputFileName); +#endif if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { *errorString = f.errorString(); return false; @@ -445,10 +449,12 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR return false; } +#if QT_CONFIG(temporaryfile) if (!f.commit()) { *errorString = f.errorString(); return false; } +#endif return true; } diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 1bfa2f0e98..6e96b88c0c 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -364,7 +364,11 @@ static bool saveUnitAsCpp(const QString &inputFileName, const QString &outputFil const QV4::CompiledData::SaveableUnitPointer &unit, QString *errorString) { +#if QT_CONFIG(temporaryfile) QSaveFile f(outputFileName); +#else + QFile f(outputFileName); +#endif if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { *errorString = f.errorString(); return false; @@ -422,10 +426,12 @@ static bool saveUnitAsCpp(const QString &inputFileName, const QString &outputFil if (!writeStr("};\n}\n}\n")) return false; +#if QT_CONFIG(temporaryfile) if (!f.commit()) { *errorString = f.errorString(); return false; } +#endif return true; } diff --git a/tools/qmlcachegen/qtquickcompiler.prf b/tools/qmlcachegen/qtquickcompiler.prf index 24cbe7f2e5..2f98aadefe 100644 --- a/tools/qmlcachegen/qtquickcompiler.prf +++ b/tools/qmlcachegen/qtquickcompiler.prf @@ -1,15 +1,5 @@ if(qtc_run|lupdate_run): return() -!contains(QT, qml) { - qt_modules = \ - $$replace(QT, -private$, _private) \ - $$replace(QT_PRIVATE, -private$, _private) - qt_modules = $$resolve_depends(qt_modules, "QT.", ".depends" ".run_depends") - !contains(qt_modules, qml): \ - error("The qtquickcompiler feature cannot be used without the QML module.") - unset(qt_modules) -} - qtPrepareTool(QML_CACHEGEN, qmlcachegen, _FILTER) qtPrepareTool(QMAKE_RCC, rcc, _DEP) diff --git a/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in new file mode 100644 index 0000000000..6cdfaf8f6f --- /dev/null +++ b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in @@ -0,0 +1,184 @@ +include(CMakeParseArguments) + +function(QT5_IMPORT_QML_PLUGINS target) +!!IF !isEmpty(CMAKE_STATIC_TYPE) + set(options) + set(oneValueArgs \"PATH_TO_SCAN\") + set(multiValueArgs) + + cmake_parse_arguments(arg \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN}) + if(NOT arg_PATH_TO_SCAN) + set(arg_PATH_TO_SCAN \"${CMAKE_CURRENT_SOURCE_DIR}\") + endif() + + # Find location of qmlimportscanner. + find_package(Qt5 COMPONENTS Core) +!!IF isEmpty(CMAKE_BIN_DIR_IS_ABSOLUTE) + set(tool_path + \"${_qt5Core_install_prefix}/$${CMAKE_BIN_DIR}qmlimportscanner$$CMAKE_BIN_SUFFIX\") +!!ELSE + set(tool_path \"$${CMAKE_BIN_DIR}qmlimportscanner$$CMAKE_BIN_SUFFIX\") +!!ENDIF + if(NOT EXISTS \"${tool_path}\" ) + message(FATAL_ERROR \"The package \\\"Qt5QmlImportScannerConfig\\\" references the file + \\\"${tool_path}\\\" +but this file does not exist. Possible reasons include: +* The file was deleted, renamed, or moved to another location. +* An install or uninstall procedure did not complete successfully. +* The installation package was faulty. +\") + endif() + + # Find location of qml dir. +!!IF isEmpty(CMAKE_QML_DIR_IS_ABSOLUTE) + set(qml_path \"${_qt5Core_install_prefix}/$${CMAKE_QML_DIR}\") +!!ELSE + set(qml_path \"$${CMAKE_QML_DIR}\") +!!ENDIF + + # Small macro to avoid duplicating code in two different loops. + macro(_qt5_QmlImportScanner_parse_entry) + set(entry_name \"qml_import_scanner_import_${idx}\") + cmake_parse_arguments(\"entry\" + \"\" + \"CLASSNAME;NAME;PATH;PLUGIN;RELATIVEPATH;TYPE;VERSION;\" \"\" + ${${entry_name}}) + endmacro() + + # Macro used to populate the dependency link flags for a certain configuriation (debug vs + # release) of a plugin. + macro(_qt5_link_to_QmlImportScanner_library_dependencies Plugin Configuration PluginLocation + IsDebugAndRelease) + + set_property(TARGET \"${Plugin}\" APPEND PROPERTY IMPORTED_CONFIGURATIONS ${Configuration}) + set(_imported_location \"${PluginLocation}\") + _qt5_Core_check_file_exists(\"${_imported_location}\") + set_target_properties(\"${Plugin}\" PROPERTIES + \"IMPORTED_LOCATION_${Configuration}\" \"${_imported_location}\" + ) + + set(_static_deps + ${_Qt5${entry_PLUGIN}_STATIC_${Configuration}_LIB_DEPENDENCIES} + ) + + if(NOT "${IsDebugAndRelease}") + set(_genex_condition \"1\") + else() + if("${Configuration}" STREQUAL "DEBUG") + set(_genex_condition \"$<CONFIG:Debug>\") + else() + set(_genex_condition \"$<NOT:$<CONFIG:Debug>>\") + endif() + endif() + if(_static_deps) + set(_static_deps_genex \"$<${_genex_condition}:${_static_deps}>\") + target_link_libraries(${imported_target} INTERFACE \"${_static_deps_genex}\") + endif() + + set(_static_link_flags \"${_Qt5${entry_PLUGIN}_STATIC_${Configuration}_LINK_FLAGS}\") + if(NOT CMAKE_VERSION VERSION_LESS \"3.13\" AND _static_link_flags) + set(_static_link_flags_genex \"$<${_genex_condition}:${_static_link_flags}>\") + target_link_options(${imported_target} INTERFACE \"${_static_link_flags_genex}\") + endif() + endmacro() + + # Run qmlimportscanner and include the generated cmake file. + set(qml_imports_file_path + \"${CMAKE_CURRENT_BINARY_DIR}/Qt5_QmlPlugins_Imports_${target}.cmake\") + + message(STATUS \"Running qmlimportscanner to find used QML plugins. \") + execute_process(COMMAND + \"${tool_path}\" \"${arg_PATH_TO_SCAN}\" -importPath \"${qml_path}\" + -cmake-output + OUTPUT_FILE \"${qml_imports_file_path}\") + + include(\"${qml_imports_file_path}\" OPTIONAL RESULT_VARIABLE qml_imports_file_path_found) + if(NOT qml_imports_file_path_found) + message(FATAL_ERROR \"Could not find ${qml_imports_file_path} which was supposed to be generated by qmlimportscanner.\") + endif() + + # Parse the generate cmake file. + # It is possible for the scanner to find no usage of QML, in which case the import count is 0. + if(qml_import_scanner_imports_count) + set(added_plugins \"\") + foreach(idx RANGE \"${qml_import_scanner_imports_count}\") + _qt5_QmlImportScanner_parse_entry() + if(entry_PATH AND entry_PLUGIN) + # Sometimes a plugin appears multiple times with different versions. + # Make sure to process it only once. + list(FIND added_plugins \"${entry_PLUGIN}\" _index) + if(NOT _index EQUAL -1) + continue() + endif() + list(APPEND added_plugins \"${entry_PLUGIN}\") + + # Add an imported target that will contain the link libraries and link options read + # from one plugin prl file. This target will point to the actual plugin and contain + # static dependency libraries and link flags. + # By creating a target for each qml plugin, CMake will take care of link flag + # deduplication. + set(imported_target \"${target}_QmlImport_${entry_PLUGIN}\") + add_library(\"${imported_target}\" MODULE IMPORTED) + target_link_libraries(\"${target}\" PRIVATE \"${imported_target}\") + + # Read static library dependencies from the plugin .prl file. + # And then set the link flags to the library dependencies extracted from the .prl + # file. +!!IF !isEmpty(CMAKE_RELEASE_TYPE) + _qt5_Core_process_prl_file( + \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_RELEASE}.prl\" RELEASE + _Qt5${entry_PLUGIN}_STATIC_RELEASE_LIB_DEPENDENCIES + _Qt5${entry_PLUGIN}_STATIC_RELEASE_LINK_FLAGS + ) + _qt5_link_to_QmlImportScanner_library_dependencies( + \"${imported_target}\" + RELEASE + \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_RELEASE}.$$QMAKE_EXTENSION_STATICLIB\" + $${CMAKE_DEBUG_AND_RELEASE}) +!!ENDIF + +!!IF !isEmpty(CMAKE_DEBUG_TYPE) + _qt5_Core_process_prl_file( + \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_DEBUG}.prl\" DEBUG + _Qt5${entry_PLUGIN}_STATIC_DEBUG_LIB_DEPENDENCIES + _Qt5${entry_PLUGIN}_STATIC_DEBUG_LINK_FLAGS + ) + _qt5_link_to_QmlImportScanner_library_dependencies( + \"${imported_target}\" + DEBUG + \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_DEBUG}.$$QMAKE_EXTENSION_STATICLIB\" + $${CMAKE_DEBUG_AND_RELEASE}) +!!ENDIF + endif() + endforeach() + + # Generate content for plugin initialization cpp file. + set(added_imports \"\") + set(qt5_qml_import_cpp_file_content \"\") + foreach(idx RANGE \"${qml_import_scanner_imports_count}\") + _qt5_QmlImportScanner_parse_entry() + if(entry_PLUGIN) + if(entry_CLASSNAME) + list(FIND added_imports \"${entry_PLUGIN}\" _index) + if(_index EQUAL -1) + string(APPEND qt5_qml_import_cpp_file_content + \"Q_IMPORT_PLUGIN(${entry_CLASSNAME})\n\") + list(APPEND added_imports \"${entry_PLUGIN}\") + endif() + else() + message(FATAL_ERROR + \"Plugin ${entry_PLUGIN} is missing a classname entry, please add one to the qmldir file.\") + endif() + endif() + endforeach() + + # Write to the generated file, and include it as a source for the given target. + set(generated_import_cpp_path + \"${CMAKE_CURRENT_BINARY_DIR}/Qt5_QmlPlugins_Imports_${target}.cpp\") + configure_file(\"${Qt5QmlImportScanner_DIR}/Qt5QmlImportScannerTemplate.cpp.in\" + \"${generated_import_cpp_path}\" + @ONLY) + target_sources(${target} PRIVATE \"${generated_import_cpp_path}\") + endif() +!!ENDIF // !isEmpty(CMAKE_STATIC_TYPE) +endfunction() diff --git a/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in b/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in new file mode 100644 index 0000000000..4ed747a555 --- /dev/null +++ b/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in @@ -0,0 +1,5 @@ +// This file is autogenerated by CMake. It imports static plugin classes for +// static plugins used by QML imports. +#include <QtPlugin> + +@qt5_qml_import_cpp_file_content@ diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 05d1f7fdc0..6d48f6203d 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -496,6 +496,36 @@ QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QString return ret; } + +QString generateCmakeIncludeFileContent(const QVariantList &importList) { + // The function assumes that "list" is a QVariantList with 0 or more QVariantMaps, where + // each map contains QString -> QVariant<QString> mappings. This matches with the structure + // that qmake parses for static qml plugin auto imporitng. + // So: [ {"a": "a","b": "b"}, {"c": "c"} ] + QString content; + QTextStream s(&content); + int importsCount = 0; + for (const QVariant &importVariant: importList) { + if (static_cast<QMetaType::Type>(importVariant.type()) == QMetaType::QVariantMap) { + s << QStringLiteral("set(qml_import_scanner_import_") << importsCount + << QStringLiteral(" \""); + + const QMap<QString, QVariant> &importDict = importVariant.toMap(); + for (auto it = importDict.cbegin(); it != importDict.cend(); ++it) { + s << it.key().toUpper() << QLatin1Char(';') + << it.value().toString() << QLatin1Char(';'); + } + s << QStringLiteral("\")\n"); + ++importsCount; + } + } + if (importsCount >= 0) { + content.prepend(QString(QStringLiteral("set(qml_import_scanner_imports_count %1)\n")) + .arg(importsCount)); + } + return content; +} + } // namespace int main(int argc, char *argv[]) @@ -512,6 +542,7 @@ int main(int argc, char *argv[]) QStringList qmlRootPaths; QStringList scanFiles; QStringList qmlImportPaths; + bool generateCmakeContent = false; int i = 1; while (i < args.count()) { @@ -536,6 +567,8 @@ int main(int argc, char *argv[]) if (i >= args.count()) std::cerr << "-importPath requires an argument\n"; argReceiver = &qmlImportPaths; + } else if (arg == QLatin1String("-cmake-output")) { + generateCmakeContent = true; } else { std::cerr << qPrintable(appName) << ": Invalid argument: \"" << qPrintable(arg) << "\"\n"; @@ -562,8 +595,15 @@ int main(int argc, char *argv[]) // Find the imports! QVariantList imports = findQmlImportsRecursively(qmlRootPaths, scanFiles); - // Convert to JSON - QByteArray json = QJsonDocument(QJsonArray::fromVariantList(imports)).toJson(); - std::cout << json.constData() << std::endl; + QByteArray content; + if (generateCmakeContent) { + // Convert to CMake code + content = generateCmakeIncludeFileContent(imports).toUtf8(); + } else { + // Convert to JSON + content = QJsonDocument(QJsonArray::fromVariantList(imports)).toJson(); + } + + std::cout << content.constData() << std::endl; return 0; } diff --git a/tools/qmlimportscanner/qmlimportscanner.pro b/tools/qmlimportscanner/qmlimportscanner.pro index 0b3a03abf3..a29b582274 100644 --- a/tools/qmlimportscanner/qmlimportscanner.pro +++ b/tools/qmlimportscanner/qmlimportscanner.pro @@ -5,6 +5,51 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII SOURCES += main.cpp +load(cmake_functions) + +CMAKE_BIN_DIR = $$cmakeRelativePath($$[QT_HOST_BINS], $$[QT_INSTALL_PREFIX]) +contains(CMAKE_BIN_DIR, "^\\.\\./.*") { + CMAKE_BIN_DIR = $$[QT_HOST_BINS]/ + CMAKE_BIN_DIR_IS_ABSOLUTE = True +} + +CMAKE_QML_DIR = $$cmakeRelativePath($$[QT_INSTALL_QML/get], $$[QT_INSTALL_PREFIX]) +contains(CMAKE_QML_DIR, "^\\.\\./.*") { + CMAKE_QML_DIR = $$[QT_INSTALL_QML/get]/ + CMAKE_QML_DIR_IS_ABSOLUTE = True +} +load(qt_build_paths) + +static|staticlib:CMAKE_STATIC_TYPE = true + +# Compute the platform target suffix. +CMAKE_QML_PLUGIN_SUFFIX_RELEASE = +win32: CMAKE_QML_PLUGIN_SUFFIX_DEBUG = d +else:darwin: CMAKE_QML_PLUGIN_SUFFIX_DEBUG = _debug +else: CMAKE_QML_PLUGIN_SUFFIX_DEBUG = + +# Find out which configurations should be handled in the generated Config.cmake file. +CMAKE_DEBUG_TYPE = +CMAKE_RELEASE_TYPE = +if(qtConfig(debug_and_release)|contains(QT_CONFIG, debug, debug|release)): CMAKE_DEBUG_TYPE = debug +if(qtConfig(debug_and_release)|contains(QT_CONFIG, release, debug|release)): CMAKE_RELEASE_TYPE = release + +qtConfig(debug_and_release) { + CMAKE_DEBUG_AND_RELEASE = TRUE +} else { + CMAKE_DEBUG_AND_RELEASE = FALSE +} + +equals(QMAKE_HOST.os, Windows): CMAKE_BIN_SUFFIX = ".exe" +cmake_config_file.input = $$PWD/Qt5QmlImportScannerConfig.cmake.in +cmake_config_file.output = $$MODULE_BASE_OUTDIR/lib/cmake/Qt5QmlImportScanner/Qt5QmlImportScannerConfig.cmake +QMAKE_SUBSTITUTES += cmake_config_file + +cmake_build_integration.files = $$cmake_config_file.output $$PWD/Qt5QmlImportScannerTemplate.cpp.in +cmake_build_integration.path = $$[QT_INSTALL_LIBS]/cmake/Qt5QmlImportScanner +prefix_build: INSTALLS += cmake_build_integration +else: COPIES += cmake_build_integration + QMAKE_TARGET_DESCRIPTION = QML Import Scanner load(qt_tool) diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp index 0d1cbf5823..27939608d7 100644 --- a/tools/qmllint/findunqualified.cpp +++ b/tools/qmllint/findunqualified.cpp @@ -40,7 +40,7 @@ #include <private/qqmljsparser_p.h> #include <private/qv4codegen_p.h> -QDebug &operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc); +QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc); static QQmlJS::TypeDescriptionReader createReaderForFile(QString const &filename) { @@ -333,7 +333,9 @@ void FindUnqualifiedIDVisitor::importExportedNames(QStringRef prefix, QString na void FindUnqualifiedIDVisitor::throwRecursionDepthError() { - return; + m_colorOut.write(QStringLiteral("Error"), Error); + m_colorOut.write(QStringLiteral("Maximum statement or expression depth exceeded"), Error); + m_visitFailed = true; } bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *) @@ -449,9 +451,10 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::CaseBlock *) leaveEnvironment(); } -bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::Catch *) +bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::Catch *catchStatement) { enterEnvironment(ScopeType::JSLexicalScope, "catch"); + m_currentScope->insertJSIdentifier(catchStatement->patternElement->bindingIdentifier.toString(), QQmlJS::AST::VariableScope::Let); return true; } @@ -568,6 +571,9 @@ FindUnqualifiedIDVisitor::~FindUnqualifiedIDVisitor() = default; bool FindUnqualifiedIDVisitor::check() { + if (m_visitFailed) + return false; + // now that all ids are known, revisit any Connections whose target were perviously unknown for (auto const& outstandingConnection: m_outstandingConnections) { auto metaObject = m_qmlid2meta[outstandingConnection.targetName]; @@ -767,7 +773,7 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *) leaveEnvironment(); } -QDebug &operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc) +QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc) { QDebugStateSaver saver(dbg); dbg.nospace() << loc.startLine; diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h index 8fc8257bef..181f42f265 100644 --- a/tools/qmllint/findunqualified.h +++ b/tools/qmllint/findunqualified.h @@ -59,6 +59,7 @@ private: QSet<QPair<QString, QString>> m_alreadySeenImports; QSet<QString> m_unknownImports; ColorOutput m_colorOut; + bool m_visitFailed = false; struct OutstandingConnection {QString targetName; ScopeTree *scope; QQmlJS::AST::UiObjectDefinition *uiod;}; diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 464f3e8a6b..f0ed1f8ebe 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -100,21 +100,45 @@ static QString enquote(const QString &string) .replace(QLatin1Char('"'),QLatin1String("\\\""))); } -void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, bool extended = false) +struct QmlVersionInfo { + QString pluginImportUri; + int majorVersion; + int minorVersion; +}; + +static bool matchingImportUri(const QQmlType &ty, const QmlVersionInfo& versionInfo) { + return (versionInfo.pluginImportUri == ty.module() + && (ty.majorVersion() == versionInfo.majorVersion || ty.majorVersion() == -1)) + || ty.module().isEmpty(); +} + +void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info, bool extended = false, bool alreadyChangedModule = false) +{ + auto ty = QQmlMetaType::qmlType(meta); if (! meta || metas->contains(meta)) return; - // dynamic meta objects can break things badly - // but extended types are usually fine - const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data); - if (extended || !(mop->flags & DynamicMetaObject)) - metas->insert(meta); + if (matchingImportUri(ty, info)) { + if (!alreadyChangedModule) { + // dynamic meta objects can break things badly + // but extended types are usually fine + const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data); + if (extended || !(mop->flags & DynamicMetaObject)) + metas->insert(meta); + } else if (!ty.module().isEmpty()) { // empty module (e.g. from an attached property) would cause a (false) match; do not warn about them + qWarning() << "Circular module dependency cannot be expressed in plugin.qmltypes file" + << "Object was:" << meta->className() + << ty.module() << info.pluginImportUri; + } + } else if (!ty.module().isEmpty()) { + alreadyChangedModule = true; + } - collectReachableMetaObjects(meta->superClass(), metas); + collectReachableMetaObjects(meta->superClass(), metas, info, /*extended=*/ false, alreadyChangedModule); } -void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas) +void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info) { if (! object) return; @@ -122,7 +146,7 @@ void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *met const QMetaObject *meta = object->metaObject(); if (verbose) std::cerr << "Processing object " << qPrintable( meta->className() ) << std::endl; - collectReachableMetaObjects(meta, metas); + collectReachableMetaObjects(meta, metas, info); for (int index = 0; index < meta->propertyCount(); ++index) { QMetaProperty prop = meta->property(index); @@ -135,17 +159,18 @@ void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *met // accessing a member of oo is going to cause a segmentation fault QObject *oo = QQmlMetaType::toQObject(prop.read(object)); if (oo && !metas->contains(oo->metaObject())) - collectReachableMetaObjects(oo, metas); + collectReachableMetaObjects(oo, metas, info); currentProperty.clear(); } } } -void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet<const QMetaObject *> *metas) +void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet<const QMetaObject *> *metas, const QmlVersionInfo& info) { - collectReachableMetaObjects(ty.baseMetaObject(), metas, ty.isExtendedType()); - if (ty.attachedPropertiesType(engine)) - collectReachableMetaObjects(ty.attachedPropertiesType(engine), metas); + collectReachableMetaObjects(ty.baseMetaObject(), metas, info, ty.isExtendedType()); + if (ty.attachedPropertiesType(engine) && matchingImportUri(ty, info)) { + collectReachableMetaObjects(ty.attachedPropertiesType(engine), metas, info); + } } /* We want to add the MetaObject for 'Qt' to the list, this is a @@ -205,13 +230,13 @@ QByteArray convertToId(const QMetaObject *mo) // Collect all metaobjects for types registered with qmlRegisterType() without parameters void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet<const QMetaObject *>& metas, - QMap<QString, QSet<QQmlType>> &compositeTypes) { + QMap<QString, QSet<QQmlType>> &compositeTypes, const QmlVersionInfo &info) { const auto qmlAllTypes = QQmlMetaType::qmlAllTypes(); for (const QQmlType &ty : qmlAllTypes) { if (!metas.contains(ty.baseMetaObject())) { if (!ty.isComposite()) { - collectReachableMetaObjects(engine, ty, &metas); - } else { + collectReachableMetaObjects(engine, ty, &metas, info); + } else if (matchingImportUri(ty, info)) { compositeTypes[ty.elementName()].insert(ty); } } @@ -222,20 +247,24 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, QSet<const QMetaObject *> &noncreatables, QSet<const QMetaObject *> &singletons, QMap<QString, QSet<QQmlType>> &compositeTypes, - const QList<QQmlType> &skip = QList<QQmlType>()) + const QmlVersionInfo &info, + const QList<QQmlType> &skip = QList<QQmlType>() + ) { QSet<const QMetaObject *> metas; metas.insert(FriendlyQObject::qtMeta()); const auto qmlTypes = QQmlMetaType::qmlTypes(); for (const QQmlType &ty : qmlTypes) { + if (!matchingImportUri(ty,info)) + continue; if (!ty.isCreatable()) noncreatables.insert(ty.baseMetaObject()); if (ty.isSingleton()) singletons.insert(ty.baseMetaObject()); if (!ty.isComposite()) { qmlTypesByCppName[ty.baseMetaObject()->className()].insert(ty); - collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas); + collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas, info); } else { compositeTypes[ty.elementName()].insert(ty); } @@ -245,6 +274,8 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, // find even more QMetaObjects by instantiating QML types and running // over the instances for (const QQmlType &ty : qmlTypes) { + if (!matchingImportUri(ty, info)) + continue; if (skip.contains(ty)) continue; if (ty.isExtendedType()) @@ -274,7 +305,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, if (verbose) std::cerr << "Trying to get singleton for " << qPrintable(tyName) << " (" << qPrintable( siinfo->typeName ) << ")" << std::endl; - collectReachableMetaObjects(object, &metas); + collectReachableMetaObjects(object, &metas, info); object = QQmlEnginePrivate::get(engine)->singletonInstance<QObject*>(ty); } else { inObjectInstantiation.clear(); @@ -293,7 +324,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, if (verbose) std::cerr << "Got " << qPrintable( tyName ) << " (" << qPrintable( QString::fromUtf8(ty.typeName()) ) << ")" << std::endl; - collectReachableMetaObjects(object, &metas); + collectReachableMetaObjects(object, &metas, info); object->deleteLater(); } else { std::cerr << "Could not create " << qPrintable(tyName) << std::endl; @@ -301,7 +332,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, } } - collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate::get(engine), metas, compositeTypes); + collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate::get(engine), metas, compositeTypes, info); return metas; } @@ -1186,11 +1217,7 @@ int main(int argc, char *argv[]) QSet<const QMetaObject *> uncreatableMetas; QSet<const QMetaObject *> singletonMetas; QMap<QString, QSet<QQmlType>> defaultCompositeTypes; - QSet<const QMetaObject *> defaultReachable = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes); - QList<QQmlType> defaultTypes = QQmlMetaType::qmlTypes(); - // add some otherwise unreachable QMetaObjects - defaultReachable.insert(&QQuickMouseEvent::staticMetaObject); // QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported QSet<QByteArray> defaultReachableNames; @@ -1200,35 +1227,25 @@ int main(int argc, char *argv[]) // composite types we want to dump information of QMap<QString, QSet<QQmlType>> compositeTypes; + int majorVersion = qtQmlMajorVersion, minorVersion = qtQmlMinorVersion; if (action == Builtins) { - for (const QMetaObject *m : qAsConst(defaultReachable)) { - if (m->className() == QLatin1String("Qt")) { - metas.insert(m); - break; - } - } - } else if (pluginImportUri == QLatin1String("QtQml")) { - bool ok = false; - const uint major = pluginImportVersion.splitRef('.').at(0).toUInt(&ok, 10); - if (!ok) { - std::cerr << "Malformed version string \""<< qPrintable(pluginImportVersion) << "\"." - << std::endl; - return EXIT_INVALIDARGUMENTS; - } - if (major != qtQmlMajorVersion) { - std::cerr << "Unsupported version \"" << qPrintable(pluginImportVersion) - << "\": Major version number must be \"" << qtQmlMajorVersion << "\"." - << std::endl; - return EXIT_INVALIDARGUMENTS; - } - metas = defaultReachable; - for (const QMetaObject *m : qAsConst(defaultReachable)) { - if (m->className() == QLatin1String("Qt")) { - metas.remove(m); - break; - } - } + QSet<const QMetaObject *> builtins = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes, {QLatin1String("Qt"), majorVersion, minorVersion}); + Q_ASSERT(builtins.size() == 1); + metas.insert(*builtins.begin()); } else { + auto versionSplitted = pluginImportVersion.split("."); + bool ok = versionSplitted.size() == 2; + if (!ok) + qCritical("Invalid version number"); + else { + majorVersion = versionSplitted.at(0).toInt(&ok); + if (!ok) + qCritical("Invalid major version"); + minorVersion = versionSplitted.at(1).toInt(&ok); + if (!ok) + qCritical("Invalid minor version"); + } + QList<QQmlType> defaultTypes = QQmlMetaType::qmlTypes(); // find a valid QtQuick import QByteArray importCode; QQmlType qtObjectType = QQmlMetaType::qmlType(&QObject::staticMetaObject); @@ -1273,8 +1290,7 @@ int main(int argc, char *argv[]) } } - QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, defaultTypes); - candidates.subtract(defaultReachable); + QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, {pluginImportUri, majorVersion, minorVersion}, defaultTypes); for (QString iter: compositeTypes.keys()) { if (defaultCompositeTypes.contains(iter)) { @@ -1284,13 +1300,8 @@ int main(int argc, char *argv[]) } } - // Also eliminate meta objects with the same classname. - // This is required because extended objects seem not to share - // a single meta object instance. - for (const QMetaObject *mo : qAsConst(defaultReachable)) - defaultReachableNames.insert(QByteArray(mo->className())); for (const QMetaObject *mo : qAsConst(candidates)) { - if (!defaultReachableNames.contains(mo->className())) + if (mo->className() != QLatin1String("Qt")) metas.insert(mo); } } |