aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/qmlcachegen/generateloader.cpp6
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp6
-rw-r--r--tools/qmlcachegen/qtquickcompiler.prf10
-rw-r--r--tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in184
-rw-r--r--tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in5
-rw-r--r--tools/qmlimportscanner/main.cpp46
-rw-r--r--tools/qmlimportscanner/qmlimportscanner.pro45
-rw-r--r--tools/qmllint/findunqualified.cpp14
-rw-r--r--tools/qmllint/findunqualified.h1
-rw-r--r--tools/qmlplugindump/main.cpp133
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);
}
}