aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/Qt6QmlBuildInternals.cmake7
-rw-r--r--src/qml/Qt6QmlMacros.cmake13
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc7
-rw-r--r--src/qml/qml/qqmlengine.cpp3
-rw-r--r--src/qml/qml/qqmlimport.cpp124
-rw-r--r--src/qml/qml/qqmlimport_p.h4
-rw-r--r--src/qml/qml/qqmlmetatype.cpp21
-rw-r--r--src/qml/qml/qqmlmetatype_p.h12
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp21
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h5
10 files changed, 144 insertions, 73 deletions
diff --git a/src/qml/Qt6QmlBuildInternals.cmake b/src/qml/Qt6QmlBuildInternals.cmake
index 7ef66ce766..aeec04e810 100644
--- a/src/qml/Qt6QmlBuildInternals.cmake
+++ b/src/qml/Qt6QmlBuildInternals.cmake
@@ -17,6 +17,7 @@ include_guard(GLOBAL)
# VERSION: Version of the qml module
# SKIP_TYPE_REGISTRATION: All qml files are expected to be registered by the
# c++ plugin code.
+# PLUGIN_OPTIONAL: Any plugins are optional
#
function(qt_add_qml_module target)
@@ -25,6 +26,7 @@ function(qt_add_qml_module target)
DESIGNER_SUPPORTED
DO_NOT_INSTALL
SKIP_TYPE_REGISTRATION
+ PLUGIN_OPTIONAL
)
set(qml_module_single_args
@@ -103,6 +105,10 @@ function(qt_add_qml_module target)
set(skip_registration_arg SKIP_TYPE_REGISTRATION)
endif()
+ if (arg_PLUGIN_OPTIONAL)
+ set(plugin_optional_arg PLUGIN_OPTIONAL)
+ endif()
+
if (arg_GENERATE_QMLTYPES)
set(generate_qmltypes_arg GENERATE_QMLTYPES)
endif()
@@ -113,6 +119,7 @@ function(qt_add_qml_module target)
${designer_supported_arg}
${no_create_option}
${skip_registration_arg}
+ ${plugin_optional_arg}
${classname_arg}
${generate_qmltypes_arg}
RESOURCE_PREFIX "/qt-project.org/imports"
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index 9671712bd3..43a590d310 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -68,6 +68,10 @@
# to not list any qml types. These are expected to be registered by the
# c++ plugin code instead.
#
+# PLUGIN_OPTIONAL: The plugin is marked as optional in the qmldir file. If the
+# type registration functions are already available by other means, typically
+# by linking a library proxied by the plugin, it won't be loaded.
+#
function(qt6_add_qml_module target)
@@ -76,6 +80,7 @@ function(qt6_add_qml_module target)
DESIGNER_SUPPORTED
DO_NOT_INSTALL_METADATA
SKIP_TYPE_REGISTRATION
+ PLUGIN_OPTIONAL
INSTALL_QML_FILES
)
@@ -224,7 +229,13 @@ function(qt6_add_qml_module target)
set(qmldir_file "${CMAKE_CURRENT_BINARY_DIR}/qmldir")
set_target_properties(${target} PROPERTIES QT_QML_MODULE_QMLDIR_FILE ${qmldir_file})
set(qmldir_file_contents "module ${arg_URI}\n")
- string(APPEND qmldir_file_contents "plugin ${target}\n")
+
+ if (arg_PLUGIN_OPTIONAL)
+ string(APPEND qmldir_file_contents "optional plugin ${target}\n")
+ else()
+ string(APPEND qmldir_file_contents "plugin ${target}\n")
+ endif()
+
if (arg_CLASSNAME)
string(APPEND qmldir_file_contents "classname ${arg_CLASSNAME}\n")
endif()
diff --git a/src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc b/src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc
index 396a83b821..e9dc5a043a 100644
--- a/src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc
+++ b/src/qml/doc/src/qmllanguageref/modules/qmldir.qdoc
@@ -163,11 +163,16 @@ MyScript 1.0 MyScript.js
\row
\li
\code
-plugin <Name> [<Path>]
+[optional] plugin <Name> [<Path>]
\endcode
\li Declares a plugin to be made available by the module.
\list
+ \li \c optional denotes that the plugin itself does not contain
+ any relevant code and only serves to load a library it links
+ to. If given, and if any types for the module are already
+ available, indicating that the library has been loaded by some
+ other means, QML will not load the plugin.
\li \c <Name> is the plugin library name. This is usually not the
same as the file name of the plugin binary, which is platform
dependent; e.g. the library \c MyAppTypes would produce
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 1f203fae88..381d934b8b 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -2202,7 +2202,8 @@ void QQmlEngine::setPluginPathList(const QStringList &paths)
bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
{
Q_D(QQmlEngine);
- return d->importDatabase.importDynamicPlugin(filePath, uri, QString(), QTypeRevision(), errors);
+ return d->importDatabase.importDynamicPlugin(filePath, uri, QString(), QTypeRevision(), false,
+ errors);
}
#endif
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index c17e3db8a1..1ae9505dc8 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -138,7 +138,7 @@ bool isPathAbsolute(const QString &path)
struct RegisteredPlugin {
QString uri;
- QPluginLoader* loader;
+ QPluginLoader *loader = nullptr;
};
struct StringRegisteredPluginMap : public QMap<QString, RegisteredPlugin> {
@@ -1229,30 +1229,36 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
int dynamicPluginsFound = 0;
int staticPluginsFound = 0;
-#if QT_CONFIG(library)
+ auto handleErrors = [&]() {
+ if (errors) {
+ // XXX TODO: should we leave the import plugin error alone?
+ // Here, we pop it off the top and coalesce it into this error's message.
+ // The reason is that the lower level may add url and line/column numbering information.
+ QQmlError error;
+ error.setDescription(
+ QQmlImportDatabase::tr(
+ "plugin cannot be loaded for module \"%1\": %2")
+ .arg(uri, errors->takeFirst().description()));
+ error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
+ errors->prepend(error);
+ }
+ };
+
const auto qmldirPlugins = qmldir.plugins();
for (const QQmlDirParser::Plugin &plugin : qmldirPlugins) {
- QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
- if (!resolvedFilePath.isEmpty()) {
- dynamicPluginsFound++;
- if (!database->importDynamicPlugin(resolvedFilePath, uri, typeNamespace, version, errors)) {
- if (errors) {
- // XXX TODO: should we leave the import plugin error alone?
- // Here, we pop it off the top and coalesce it into this error's message.
- // The reason is that the lower level may add url and line/column numbering information.
- QQmlError error;
- error.setDescription(
- QQmlImportDatabase::tr(
- "plugin cannot be loaded for module \"%1\": %2")
- .arg(uri, errors->takeFirst().description()));
- error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
- errors->prepend(error);
- }
- return false;
- }
+ const QString resolvedFilePath = database->resolvePlugin(
+ typeLoader, qmldirPath, plugin.path, plugin.name);
+
+ if (resolvedFilePath.isEmpty())
+ continue;
+
+ ++dynamicPluginsFound;
+ if (!database->importDynamicPlugin(
+ resolvedFilePath, uri, typeNamespace, version, plugin.optional, errors)) {
+ handleErrors();
+ return false;
}
}
-#endif // QT_CONFIG(library)
if (dynamicPluginsFound < qmldirPluginCount) {
// Check if the missing plugins can be resolved statically. We do this by looking at
@@ -2178,16 +2184,9 @@ void QQmlImportDatabase::setImportPathList(const QStringList &paths)
/*!
\internal
*/
-static bool registerPluginTypes(QObject *instance, const QString &basePath, const QString &uri,
- const QString &typeNamespace, QTypeRevision version,
- QList<QQmlError> *errors)
+static bool lockModule(const QString &uri, const QString &typeNamespace, QTypeRevision version,
+ QList<QQmlError> *errors)
{
- if (qmlImportTrace())
- qDebug().nospace() << "QQmlImportDatabase::registerPluginTypes: " << uri << " from " << basePath;
-
- if (!QQmlMetaType::registerPluginTypes(instance, basePath, uri, typeNamespace, version, errors))
- return false;
-
if (version.hasMajorVersion() && !typeNamespace.isEmpty()
&& !QQmlMetaType::protectModule(uri, version)) {
QQmlError error;
@@ -2229,8 +2228,12 @@ bool QQmlImportDatabase::importStaticPlugin(
plugin.loader = nullptr;
plugins->insert(uniquePluginID, plugin);
- if (!registerPluginTypes(instance, basePath, uri, typeNamespace, version, errors))
+ if (QQmlMetaType::registerPluginTypes(
+ instance, basePath, uri, typeNamespace, version, errors)
+ == QQmlMetaType::RegistrationResult::Failure
+ || !lockModule(uri, typeNamespace, version, errors)) {
return false;
+ }
}
// Release the lock on plugins early as we're done with the global part. Releasing the lock
@@ -2245,13 +2248,12 @@ bool QQmlImportDatabase::importStaticPlugin(
return true;
}
-#if QT_CONFIG(library)
/*!
\internal
*/
bool QQmlImportDatabase::importDynamicPlugin(
const QString &filePath, const QString &uri, const QString &typeNamespace,
- QTypeRevision version, QList<QQmlError> *errors)
+ QTypeRevision version, bool isOptional, QList<QQmlError> *errors)
{
QFileInfo fileInfo(filePath);
const QString absoluteFilePath = fileInfo.absoluteFilePath();
@@ -2279,35 +2281,56 @@ bool QQmlImportDatabase::importDynamicPlugin(
return false;
}
- QPluginLoader* loader = nullptr;
- if (!typesRegistered) {
- loader = new QPluginLoader(absoluteFilePath);
+ RegisteredPlugin plugin;
+ plugin.uri = uri;
+
+ const QString absolutePath = fileInfo.absolutePath();
+ if (!typesRegistered && isOptional) {
+ switch (QQmlMetaType::registerPluginTypes(nullptr, absolutePath, uri, typeNamespace,
+ version, errors)) {
+ case QQmlMetaType::RegistrationResult::NoRegistrationFunction:
+ // try again with plugin
+ break;
+ case QQmlMetaType::RegistrationResult::Success:
+ if (!lockModule(uri, typeNamespace, version, errors))
+ return false;
+ // instance and loader intentionally left at nullptr
+ plugins->insert(absoluteFilePath, plugin);
+ typesRegistered = true;
+ break;
+ case QQmlMetaType::RegistrationResult::Failure:
+ return false;
+ }
+ }
- if (!loader->load()) {
+#if QT_CONFIG(library)
+ if (!typesRegistered) {
+ plugin.loader = new QPluginLoader(absoluteFilePath);
+ if (!plugin.loader->load()) {
if (errors) {
QQmlError error;
- error.setDescription(loader->errorString());
+ error.setDescription(plugin.loader->errorString());
errors->prepend(error);
}
- delete loader;
+ delete plugin.loader;
return false;
}
- } else {
- loader = plugins->value(absoluteFilePath).loader;
- }
- instance = loader->instance();
-
- if (!typesRegistered) {
- RegisteredPlugin plugin;
- plugin.uri = uri;
- plugin.loader = loader;
+ instance = plugin.loader->instance();
plugins->insert(absoluteFilePath, plugin);
// Continue with shared code path for dynamic and static plugins:
- if (!registerPluginTypes(instance, fileInfo.absolutePath(), uri, typeNamespace, version, errors))
+ if (QQmlMetaType::registerPluginTypes(
+ instance, fileInfo.absolutePath(), uri, typeNamespace, version, errors)
+ == QQmlMetaType::RegistrationResult::Failure
+ || !lockModule(uri, typeNamespace, version, errors)) {
return false;
+ }
+ } else {
+ if (QPluginLoader *loader = plugins->value(absoluteFilePath).loader)
+ instance = loader->instance();
}
+#endif // QT_CONFIG(library)
}
// Release the lock on plugins early as we're done with the global part. Releasing the lock
@@ -2335,10 +2358,12 @@ bool QQmlImportDatabase::removeDynamicPlugin(const QString &filePath)
if (!loader)
return false;
+#if QT_CONFIG(library)
if (!loader->unload()) {
qWarning("Unloading %s failed: %s", qPrintable(it->uri),
qPrintable(loader->errorString()));
}
+#endif
delete loader;
plugins->erase(it);
@@ -2356,7 +2381,6 @@ QStringList QQmlImportDatabase::dynamicPlugins() const
}
return results;
}
-#endif // QT_CONFIG(library)
void QQmlImportDatabase::clearDirCache()
{
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index 57baa2c87f..5f16880199 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -226,13 +226,11 @@ public:
QQmlImportDatabase(QQmlEngine *);
~QQmlImportDatabase();
-#if QT_CONFIG(library)
bool importDynamicPlugin(const QString &filePath, const QString &uri,
const QString &importNamespace, QTypeRevision version,
- QList<QQmlError> *errors);
+ bool isOptional, QList<QQmlError> *errors);
bool removeDynamicPlugin(const QString &filePath);
QStringList dynamicPlugins() const;
-#endif
QStringList importPathList(PathType type = LocalOrRemote) const;
void setImportPathList(const QStringList &paths);
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 61e8e323c6..fd307e4575 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -681,9 +681,9 @@ public:
};
-bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePath,
- const QString &uri, const QString &typeNamespace,
- QTypeRevision version, QList<QQmlError> *errors)
+QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes(
+ QObject *instance, const QString &basePath, const QString &uri,
+ const QString &typeNamespace, QTypeRevision version, QList<QQmlError> *errors)
{
if (!typeNamespace.isEmpty() && typeNamespace != uri) {
// This is an 'identified' module
@@ -695,7 +695,7 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat
.arg(typeNamespace).arg(uri));
errors->prepend(error);
}
- return false;
+ return RegistrationResult::Failure;
}
QStringList failures;
@@ -713,7 +713,7 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat
.arg(typeNamespace));
errors->prepend(error);
}
- return false;
+ return RegistrationResult::Failure;
}
} else {
// This is not an identified module - provide a warning
@@ -722,7 +722,7 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat
"it cannot be protected from external registrations.").arg(uri));
}
- if (!qobject_cast<QQmlEngineExtensionInterface *>(instance)) {
+ if (instance && !qobject_cast<QQmlEngineExtensionInterface *>(instance)) {
QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance);
if (!iface) {
if (errors) {
@@ -732,7 +732,7 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat
"QQmlEngineExtensionInterface").arg(typeNamespace));
errors->prepend(error);
}
- return false;
+ return RegistrationResult::Failure;
}
if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) {
@@ -746,7 +746,8 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat
iface->registerTypes(moduleId);
}
- data->registerModuleTypes(uri);
+ if (failures.isEmpty() && !data->registerModuleTypes(uri))
+ return RegistrationResult::NoRegistrationFunction;
if (!failures.isEmpty()) {
if (errors) {
@@ -756,11 +757,11 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat
errors->prepend(error);
}
}
- return false;
+ return RegistrationResult::Failure;
}
}
- return true;
+ return RegistrationResult::Success;
}
/*
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index 08d79d2e5b..9ce33ab545 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -73,14 +73,20 @@ struct CompositeMetaTypeIds
class Q_QML_PRIVATE_EXPORT QQmlMetaType
{
public:
+ enum class RegistrationResult {
+ Success,
+ Failure,
+ NoRegistrationFunction
+ };
+
static QQmlType registerType(const QQmlPrivate::RegisterType &type);
static QQmlType registerInterface(const QQmlPrivate::RegisterInterface &type);
static QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &type);
static QQmlType registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type);
static QQmlType registerCompositeType(const QQmlPrivate::RegisterCompositeType &type);
- static bool registerPluginTypes(QObject *instance, const QString &basePath,
- const QString &uri, const QString &typeNamespace,
- QTypeRevision version, QList<QQmlError> *errors);
+ static RegistrationResult registerPluginTypes(QObject *instance, const QString &basePath,
+ const QString &uri, const QString &typeNamespace,
+ QTypeRevision version, QList<QQmlError> *errors);
static QQmlType typeForUrl(const QString &urlString, const QHashedStringRef& typeName,
bool isCompositeSingleton, QList<QQmlError> *errors,
QTypeRevision version = QTypeRevision());
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
index cb16d2e57a..a8a450d2ca 100644
--- a/src/qml/qmldirparser/qqmldirparser.cpp
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -178,15 +178,32 @@ bool QQmlDirParser::parse(const QString &source)
} 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));
+ QStringLiteral("plugin directive requires one or two arguments, but %1 were provided")
+ .arg(sectionCount - 1));
continue;
}
- const Plugin entry(sections[1], sections[2]);
+ 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"));
+ continue;
+ }
+
+ if (sectionCount < 3 || sectionCount > 4) {
+ reportError(lineNumber, 0,
+ QStringLiteral("plugin directive requires one or two arguments, but %1 were provided")
+ .arg(sectionCount - 2));
+ continue;
+ }
+
+ const Plugin entry(sections[2], sections[3], true);
+ _plugins.append(entry);
+
} else if (sections[0] == QLatin1String("classname")) {
if (sectionCount < 2) {
reportError(lineNumber, 0,
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
index ccdac8f484..a5a548daca 100644
--- a/src/qml/qmldirparser/qqmldirparser_p.h
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -88,14 +88,15 @@ public:
{
Plugin() = default;
- Plugin(const QString &name, const QString &path)
- : name(name), path(path)
+ Plugin(const QString &name, const QString &path, bool optional)
+ : name(name), path(path), optional(optional)
{
checkNonRelative("Plugin", name, path);
}
QString name;
QString path;
+ bool optional = false;
};
struct Component