diff options
-rw-r--r-- | src/qml/Qt6QmlBuildInternals.cmake | 2 | ||||
-rw-r--r-- | src/qml/Qt6QmlMacros.cmake | 42 | ||||
-rw-r--r-- | src/qml/qml/qqml.cpp | 8 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 6 | ||||
-rw-r--r-- | src/qml/qmldirparser/qqmldirparser.cpp | 105 | ||||
-rw-r--r-- | src/qml/qmldirparser/qqmldirparser_p.h | 17 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsimporter.cpp | 10 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/Things/qmldir | 1 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/data/optionalImport.qml | 5 | ||||
-rw-r--r-- | tests/auto/qml/qmllint/tst_qmllint.cpp | 1 | ||||
-rw-r--r-- | tools/qmlimportscanner/main.cpp | 2 |
12 files changed, 123 insertions, 78 deletions
diff --git a/src/qml/Qt6QmlBuildInternals.cmake b/src/qml/Qt6QmlBuildInternals.cmake index d74b1d55fe..0e702feea2 100644 --- a/src/qml/Qt6QmlBuildInternals.cmake +++ b/src/qml/Qt6QmlBuildInternals.cmake @@ -39,6 +39,7 @@ function(qt_internal_add_qml_module target) set(qml_module_multi_args IMPORTS + OPTIONAL_IMPORTS TYPEINFO DEPENDENCIES ) @@ -134,6 +135,7 @@ function(qt_internal_add_qml_module target) VERSION ${arg_VERSION} QML_FILES ${arg_QML_FILES} IMPORTS "${arg_IMPORTS}" + OPTIONAL_IMPORTS "${arg_OPTIONAL_IMPORTS}" TYPEINFO "${arg_TYPEINFO}" DO_NOT_INSTALL_METADATA DO_NOT_CREATE_TARGET diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index 4bd457311a..0afd365948 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -63,6 +63,11 @@ # as version to forward the version the current module is being imported with, # e.g. QtQuick/auto. (OPTIONAL) # +# OPTIONAL_IMPORTS: List of other Qml Modules that this module may import at +# run-time. Those are not automatically imported by the QML engine when +# importing the current module, but rather serve as hints to tools like +# qmllint. Versions can be specified in the same as for IMPORT. (OPTIONAL) +# # RESOURCE_EXPORT: In static builds, when Qml files are processed via the Qt # Quick Compiler generate a separate static library that will be linked in # as an Interface. Supply an output variable to perform any custom actions @@ -109,6 +114,7 @@ function(qt6_add_qml_module target) SOURCES QML_FILES IMPORTS + OPTIONAL_IMPORTS DEPENDENCIES ) @@ -283,23 +289,29 @@ function(qt6_add_qml_module target) # plugins.qmltypes is actually generated. string(APPEND qmldir_file_contents "typeinfo plugins.qmltypes\n") endif() - foreach(import IN LISTS arg_IMPORTS) - string(FIND ${import} "/" slash_position REVERSE) - if (slash_position EQUAL -1) - string(APPEND qmldir_file_contents "import ${import}\n") - else() - string(SUBSTRING ${import} 0 ${slash_position} import_module) - math(EXPR slash_position "${slash_position} + 1") - string(SUBSTRING ${import} ${slash_position} -1 import_version) - if (import_version MATCHES "[0-9]+\\.[0-9]+" OR import_version MATCHES "[0-9]+") - string(APPEND qmldir_file_contents "import ${import_module} ${import_version}\n") - elseif (import_version MATCHES "auto") - string(APPEND qmldir_file_contents "import ${import_module} auto\n") + + macro(_add_imports imports import_string) + foreach(import IN LISTS ${imports}) + string(FIND ${import} "/" slash_position REVERSE) + if (slash_position EQUAL -1) + string(APPEND qmldir_file_contents "${import_string} ${import}\n") else() - message(FATAL_ERROR "Invalid module import version number. Expected 'VersionMajor', 'VersionMajor.VersionMinor' or 'auto'.") + string(SUBSTRING ${import} 0 ${slash_position} import_module) + math(EXPR slash_position "${slash_position} + 1") + string(SUBSTRING ${import} ${slash_position} -1 import_version) + if (import_version MATCHES "[0-9]+\\.[0-9]+" OR import_version MATCHES "[0-9]+") + string(APPEND qmldir_file_contents "${import_string} ${import_module} ${import_version}\n") + elseif (import_version MATCHES "auto") + string(APPEND qmldir_file_contents "${import_string} ${import_module} auto\n") + else() + message(FATAL_ERROR "Invalid module ${import_string} version number. Expected 'VersionMajor', 'VersionMajor.VersionMinor' or 'auto'.") + endif() endif() - endif() - endforeach() + endforeach() + endmacro() + + _add_imports(arg_IMPORTS "import") + _add_imports(arg_OPTIONAL_IMPORTS "optional import") foreach(dependency IN LISTS arg_DEPENDENCIES) string(FIND ${dependency} "/" slash_position REVERSE) diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index d26a09b081..e61cf417e3 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -75,12 +75,12 @@ void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor) static QQmlDirParser::Import resolveImport(const QString &uri, int importMajor, int importMinor) { if (importMajor == QQmlModuleImportAuto) - return QQmlDirParser::Import(uri, QTypeRevision(), true); + return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Auto); else if (importMajor == QQmlModuleImportLatest) - return QQmlDirParser::Import(uri, QTypeRevision(), false); + return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Default); else if (importMinor == QQmlModuleImportLatest) - return QQmlDirParser::Import(uri, QTypeRevision::fromMajorVersion(importMajor), false); - return QQmlDirParser::Import(uri, QTypeRevision::fromVersion(importMajor, importMinor), false); + return QQmlDirParser::Import(uri, QTypeRevision::fromMajorVersion(importMajor), QQmlDirParser::Import::Default); + return QQmlDirParser::Import(uri, QTypeRevision::fromVersion(importMajor, importMinor), QQmlDirParser::Import::Default); } static QTypeRevision resolveModuleVersion(int moduleMajor) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 16b05898d1..71fc753c3b 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -724,7 +724,7 @@ void QQmlMetaType::registerModuleImport(const QString &uri, QTypeRevision module static bool operator==(const QQmlDirParser::Import &a, const QQmlDirParser::Import &b) { - return a.module == b.module && a.version == b.version && a.isAutoImport == b.isAutoImport; + return a.module == b.module && a.version == b.version && a.flags == b.flags; } void QQmlMetaType::unregisterModuleImport(const QString &uri, QTypeRevision moduleVersion, diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 6b4ce26c97..1b14467266 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -742,11 +742,13 @@ bool QQmlTypeLoader::Blob::loadImportDependencies(PendingImportPtr currentImport = QQmlMetaType::moduleImports(currentImport->uri, currentImport->version) + qmldir.imports(); for (const auto &implicitImport : implicitImports) { + if (implicitImport.flags & QQmlDirParser::Import::Optional) + continue; auto dependencyImport = std::make_shared<PendingImport>(); dependencyImport->uri = implicitImport.module; dependencyImport->qualifier = currentImport->qualifier; - dependencyImport->version = implicitImport.isAutoImport ? currentImport->version - : implicitImport.version; + dependencyImport->version = (implicitImport.flags & QQmlDirParser::Import::Auto) + ? currentImport->version : implicitImport.version; if (!addImport(dependencyImport, QQmlImports::ImportLowPrecedence, errors)) { QQmlError error; error.setDescription( diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp index 0588e5bca7..403c04b1e1 100644 --- a/src/qml/qmldirparser/qqmldirparser.cpp +++ b/src/qml/qmldirparser/qqmldirparser.cpp @@ -110,6 +110,50 @@ bool QQmlDirParser::parse(const QString &source) quint16 lineNumber = 0; bool firstLine = true; + auto readImport = [&](const QString *sections, int sectionCount, Import::Flags flags) { + Import import; + if (sectionCount == 2) { + import = Import(sections[1], QTypeRevision(), flags); + } else if (sectionCount == 3) { + if (sections[2] == QLatin1String("auto")) { + import = Import(sections[1], QTypeRevision(), flags | Import::Auto); + } else { + const auto version = parseVersion(sections[2]); + if (version.isValid()) { + import = Import(sections[1], version, flags); + } else { + reportError(lineNumber, 0, + QStringLiteral("invalid version %1, expected <major>.<minor>") + .arg(sections[2])); + return false; + } + } + } else { + reportError(lineNumber, 0, + QStringLiteral("%1 requires 1 or 2 arguments, but %2 were provided") + .arg(sections[0]).arg(sectionCount - 1)); + return false; + } + if (sections[0] == QStringLiteral("import")) + _imports.append(import); + else + _dependencies.append(import); + return true; + }; + + auto readPlugin = [&](const QString *sections, int sectionCount, bool isOptional) { + if (sectionCount < 2 || sectionCount > 3) { + reportError(lineNumber, 0, QStringLiteral("plugin directive requires one or two " + "arguments, but %1 were provided") + .arg(sectionCount - 1)); + return false; + } + + const Plugin entry(sections[1], sections[2], isOptional); + _plugins.append(entry); + return true; + }; + const QChar *ch = source.constData(); while (!ch->isNull()) { ++lineNumber; @@ -176,34 +220,26 @@ bool QQmlDirParser::parse(const QString &source) _typeNamespace = sections[1]; } else if (sections[0] == QLatin1String("plugin")) { - if (sectionCount < 2 || sectionCount > 3) { - reportError(lineNumber, 0, - QStringLiteral("plugin directive requires one or two arguments, but %1 were provided") - .arg(sectionCount - 1)); - + if (!readPlugin(sections, sectionCount, false)) continue; - } - - const Plugin entry(sections[1], sections[2], false); - - _plugins.append(entry); - } else if (sections[0] == QLatin1String("optional")) { - if (sectionCount < 2 || sections[1] != QLatin1String("plugin")) { - reportError(lineNumber, 0, QStringLiteral("only plugins can be optional")); + if (sectionCount < 2) { + reportError(lineNumber, 0, QStringLiteral("optional directive requires further " + "arguments, but none were provided.")); continue; } - if (sectionCount < 3 || sectionCount > 4) { - reportError(lineNumber, 0, - QStringLiteral("plugin directive requires one or two arguments, but %1 were provided") - .arg(sectionCount - 2)); + if (sections[1] == QStringLiteral("plugin")) { + if (!readPlugin(sections + 1, sectionCount - 1, true)) + continue; + } else if (sections[1] == QLatin1String("import")) { + if (!readImport(sections + 1, sectionCount - 1, Import::Optional)) + continue; + } else { + reportError(lineNumber, 0, QStringLiteral("only import and plugin can be optional, " + "not %1.").arg(sections[1])); continue; } - - const Plugin entry(sections[2], sections[3], true); - _plugins.append(entry); - } else if (sections[0] == QLatin1String("classname")) { if (sectionCount < 2) { reportError(lineNumber, 0, @@ -261,33 +297,8 @@ bool QQmlDirParser::parse(const QString &source) _designerSupported = true; } else if (sections[0] == QLatin1String("import") || sections[0] == QLatin1String("depends")) { - Import import; - if (sectionCount == 2) { - import = Import(sections[1], QTypeRevision(), false); - } else if (sectionCount == 3) { - if (sections[2] == QLatin1String("auto")) { - import = Import(sections[1], QTypeRevision(), true); - } else { - const auto version = parseVersion(sections[2]); - if (version.isValid()) { - import = Import(sections[1], version, false); - } else { - reportError(lineNumber, 0, - QStringLiteral("invalid version %1, expected <major>.<minor>") - .arg(sections[2])); - continue; - } - } - } else { - reportError(lineNumber, 0, - QStringLiteral("%1 requires 1 or 2 arguments, but %2 were provided") - .arg(sections[0]).arg(sectionCount - 1)); + if (!readImport(sections, sectionCount, Import::Default)) continue; - } - if (sections[0] == QStringLiteral("import")) - _imports.append(import); - else - _dependencies.append(import); } else if (sectionCount == 2) { // No version specified (should only be used for relative qmldir files) const Component entry(sections[0], sections[1], QTypeRevision()); diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h index 48f808ff12..098b15a51b 100644 --- a/src/qml/qmldirparser/qqmldirparser_p.h +++ b/src/qml/qmldirparser/qqmldirparser_p.h @@ -134,13 +134,22 @@ public: struct Import { + enum Flag { + Default = 0x0, + Auto = 0x1, // forward the version of the importing module + Optional = 0x2 // is not automatically imported but only a tooling hint + }; + Q_DECLARE_FLAGS(Flags, Flag) + Import() = default; - Import(QString module, QTypeRevision version, bool isAutoImport) - : module(module), version(version), isAutoImport(isAutoImport) {} + Import(QString module, QTypeRevision version, Flags flags) + : module(module), version(version), flags(flags) + { + } QString module; - QTypeRevision version; // default: lastest version - bool isAutoImport = false; // if set: forward the version of the importing module + QTypeRevision version; // invalid version is latest version, unless Flag::Auto + Flags flags; }; QMultiHash<QString,Component> components() const; diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp index 73d7637d79..e33538b653 100644 --- a/src/qmlcompiler/qqmljsimporter.cpp +++ b/src/qmlcompiler/qqmljsimporter.cpp @@ -89,14 +89,15 @@ void QQmlJSImporter::readQmltypes( for (const QString &dependency : qAsConst(dependencyStrings)) { const auto blank = dependency.indexOf(u' '); if (blank < 0) { - dependencies->append(QQmlDirParser::Import(dependency, {}, false)); + dependencies->append(QQmlDirParser::Import(dependency, {}, + QQmlDirParser::Import::Default)); continue; } const QString module = dependency.left(blank); const QString versionString = dependency.mid(blank + 1).trimmed(); if (versionString == QStringLiteral("auto")) { - dependencies->append(QQmlDirParser::Import(module, {}, true)); + dependencies->append(QQmlDirParser::Import(module, {}, QQmlDirParser::Import::Auto)); continue; } @@ -107,7 +108,8 @@ void QQmlJSImporter::readQmltypes( : QTypeRevision::fromVersion(versionString.left(dot).toUShort(), versionString.mid(dot + 1).toUShort()); - dependencies->append(QQmlDirParser::Import(module, version, false)); + dependencies->append(QQmlDirParser::Import(module, version, + QQmlDirParser::Import::Default)); } } @@ -174,7 +176,7 @@ void QQmlJSImporter::importDependencies( for (auto const &import : qAsConst(import.imports)) { importHelper(import.module, types, prefix, - import.isAutoImport ? version : import.version); + (import.flags & QQmlDirParser::Import::Auto) ? version : import.version); } } diff --git a/tests/auto/qml/qmllint/data/Things/qmldir b/tests/auto/qml/qmllint/data/Things/qmldir index 554f75d313..3c48ae0648 100644 --- a/tests/auto/qml/qmllint/data/Things/qmldir +++ b/tests/auto/qml/qmllint/data/Things/qmldir @@ -3,3 +3,4 @@ Something 1.0 SomethingElse.qml typeinfo plugins.qmltypes depends QtQuick 2.0 import QtQml +optional import QtQuick.LocalStorage auto diff --git a/tests/auto/qml/qmllint/data/optionalImport.qml b/tests/auto/qml/qmllint/data/optionalImport.qml new file mode 100644 index 0000000000..2be7cd444a --- /dev/null +++ b/tests/auto/qml/qmllint/data/optionalImport.qml @@ -0,0 +1,5 @@ +import Things 6.0 + +QtObject { + property var db: LocalStorage.openDatabaseSync("foo", "2", "bar", 4) +} diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 09ffd2c9ec..4c18253b0c 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -282,6 +282,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("enumFromQtQml") << QStringLiteral("enumFromQtQml.qml"); QTest::newRow("anchors1") << QStringLiteral("anchors1.qml"); QTest::newRow("anchors2") << QStringLiteral("anchors2.qml"); + QTest::newRow("optionalImport") << QStringLiteral("optionalImport.qml"); } void TestQmllint::cleanQmlCode() diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 5729c74855..70dec3ed96 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -191,7 +191,7 @@ QVariantMap pluginsForModulePath(const QString &modulePath, const QString &versi const auto imports = parser.imports(); for (const auto &import : imports) { - if (import.isAutoImport) { + if (import.flags & QQmlDirParser::Import::Auto) { importsAndDependencies.append( import.module + QLatin1Char(' ') + (version.isEmpty() ? QString::fromLatin1("auto") : version)); |