diff options
author | Alexey Edelev <alexey.edelev@qt.io> | 2021-10-18 12:50:17 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2021-11-02 10:34:32 +0000 |
commit | f44aabad32c6b2b905fe684217a1beab8476ceac (patch) | |
tree | a0e055baebc4a6722e8fcafc7eb032985d1b4ac1 | |
parent | 5ef69f7cf442c798721ce1a22ab21f359e741a7e (diff) |
Use correct plugin name for Android QML plugins
The qmldir 'plugin' directive should contain the full plugin name
including uri when targeting Android. Qt for Android previously had
the special handling of this directive when loading plugins. This
removes this special handling and generates the correct Android plugin
name in qmldir file at configuring time to load the plugin without
patching the name.
Old mechanism is marked as deprecated since 6.2.2.
This fixes the user static plugin in CMake project case, but does not
fix the user shared plugin loading.
[ChangeLog][QtQml] Starting with Qt 6.4, QML import mechanism won't add
URI-based prefixes when looking for QML plugins for Android
applications. For forward compatibility, the 'plugin' record of the
qmldir files should contain the full file name of the plugin without
abi-specific suffixes and the file extension. When using the CMake API
to create the qmldir, no further action is necessary.
Task-number: QTBUG-95984
Task-number: QTBUG-96898
Change-Id: I270d444ddcafab7049b160d5cdc278faad2ec461
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
(cherry picked from commit 36e9a36b0d2fe7fd27378944218f15e1c96af925)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/qml/Qt6QmlMacros.cmake | 131 | ||||
-rw-r--r-- | src/qml/qml/qqmlpluginimporter.cpp | 52 |
2 files changed, 113 insertions, 70 deletions
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index a75a8e794b..5ffc39d0ca 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -532,21 +532,19 @@ function(qt6_add_qml_module target) PROPERTIES QT_RESOURCE_ALIAS "qmldir" ) - if(NOT ANDROID) - foreach(prefix IN LISTS prefixes) - set(resource_targets) - qt6_add_resources(${target} ${qmldir_resource_name} - FILES ${arg_OUTPUT_DIRECTORY}/qmldir - PREFIX "${prefix}" - OUTPUT_TARGETS resource_targets - ) - list(APPEND output_targets ${resource_targets}) - # If we are adding the same file twice, we need a different resource - # name for the second one. It has the same QT_RESOURCE_ALIAS but a - # different prefix, so we can't put it in the same resource. - string(APPEND qmldir_resource_name "_copy") - endforeach() - endif() + foreach(prefix IN LISTS prefixes) + set(resource_targets) + qt6_add_resources(${target} ${qmldir_resource_name} + FILES ${arg_OUTPUT_DIRECTORY}/qmldir + PREFIX "${prefix}" + OUTPUT_TARGETS resource_targets + ) + list(APPEND output_targets ${resource_targets}) + # If we are adding the same file twice, we need a different resource + # name for the second one. It has the same QT_RESOURCE_ALIAS but a + # different prefix, so we can't put it in the same resource. + string(APPEND qmldir_resource_name "_copy") + endforeach() endif() if(NOT arg_NO_PLUGIN AND NOT arg_NO_CREATE_PLUGIN_TARGET) @@ -926,8 +924,12 @@ function(_qt_internal_target_generate_qmldir target) string(APPEND content "optional ") endif() - _qt_internal_get_qml_plugin_basename(plugin_basename ${plugin_target}) - string(APPEND content "plugin ${plugin_basename}\n") + get_target_property(target_path ${target} QT_QML_MODULE_TARGET_PATH) + _qt_internal_get_qml_plugin_output_name(plugin_output_name ${plugin_target} + TARGET_PATH "${target_path}" + URI "${uri}" + ) + string(APPEND content "plugin ${plugin_output_name}\n") _qt_internal_qmldir_item(classname QT_QML_MODULE_CLASS_NAME) endif() @@ -1140,32 +1142,15 @@ function(qt6_add_qml_plugin target) ) endif() - if (ANDROID) - # Adjust Qml plugin names on Android similar to qml_plugin.prf which calls - # $$qt5LibraryTarget($$TARGET, "qml/$$TARGETPATH/"). - # Example plugin names: - # qtdeclarative - # TARGET_PATH: QtQml/Models - # file name: libqml_QtQml_Models_modelsplugin_arm64-v8a.so - # qtquickcontrols2 - # TARGET_PATH: QtQuick/Controls.2/Material - # file name: - # libqml_QtQuick_Controls.2_Material_qtquickcontrols2materialstyleplugin_arm64-v8a.so - if(NOT arg_TARGET_PATH AND TARGET "${arg_BACKING_TARGET}") - get_target_property(arg_TARGET_PATH ${arg_BACKING_TARGET} QT_QML_MODULE_TARGET_PATH) - endif() - if(arg_TARGET_PATH) - string(REPLACE "/" "_" android_plugin_name_infix_name "${arg_TARGET_PATH}") - else() - string(REPLACE "." "_" android_plugin_name_infix_name "${arg_URI}") - endif() - - _qt_internal_get_qml_plugin_basename(plugin_basename ${target}) - set(final_android_qml_plugin_name - "qml_${android_plugin_name_infix_name}_${plugin_basename}") + if(ANDROID) + _qt_internal_get_qml_plugin_output_name(plugin_output_name ${target} + BACKING_TARGET "${arg_BACKING_TARGET}" + TARGET_PATH "${arg_TARGET_PATH}" + URI "${arg_URI}" + ) set_target_properties(${target} PROPERTIES - LIBRARY_OUTPUT_NAME "${final_android_qml_plugin_name}" + LIBRARY_OUTPUT_NAME "${plugin_output_name}" ) qt6_android_apply_arch_suffix(${target}) endif() @@ -2255,17 +2240,65 @@ function(_qt_internal_add_static_qml_plugin_dependencies plugin_target backing_t endif() endfunction() -# The function returns the base output name of a qml plugin that will be used as library output -# name and in a qmldir file as the 'plugin <plugin_basename>' record. -function(_qt_internal_get_qml_plugin_basename out_var plugin_target) - set(plugin_basename) +# The function returns the output name of a qml plugin that will be used as library output +# name and in a qmldir file as the 'plugin <plugin_output_name>' record. +function(_qt_internal_get_qml_plugin_output_name out_var plugin_target) + cmake_parse_arguments(arg + "" + "BACKING_TARGET;TARGET_PATH;URI" + "" + ${ARGN} + ) + set(plugin_name) if(TARGET ${plugin_target}) - get_target_property(plugin_basename ${plugin_target} OUTPUT_NAME) + get_target_property(plugin_name ${plugin_target} OUTPUT_NAME) endif() - if(NOT plugin_basename) - set(plugin_basename "${plugin_target}") + if(NOT plugin_name) + set(plugin_name "${plugin_target}") endif() - set(${out_var} "${plugin_basename}" PARENT_SCOPE) + + if(ANDROID) + # In Android all plugins are stored in directly the /libs directory. This means that plugin + # names must be unique in scope of apk. To make this work we prepend uri-based prefix to + # each qml plugin in case if users don't use the manually written qmldir files. + get_target_property(no_generate_qmldir ${target} QT_QML_MODULE_NO_GENERATE_QMLDIR) + if(TARGET "${arg_BACKING_TARGET}") + get_target_property(no_generate_qmldir ${arg_BACKING_TARGET} + QT_QML_MODULE_NO_GENERATE_QMLDIR) + + # Adjust Qml plugin names on Android similar to qml_plugin.prf which calls + # $$qt5LibraryTarget($$TARGET, "qml/$$TARGETPATH/"). + # Example plugin names: + # qtdeclarative + # TARGET_PATH: QtQml/Models + # file name: libqml_QtQml_Models_modelsplugin_x86_64.so + # qtquickcontrols2 + # TARGET_PATH: QtQuick/Controls.2/Material + # file name: + # libqml_QtQuick_Controls.2_Material_qtquickcontrols2materialstyleplugin_x86_64.so + if(NOT arg_TARGET_PATH) + get_target_property(arg_TARGET_PATH ${arg_BACKING_TARGET} + QT_QML_MODULE_TARGET_PATH) + endif() + endif() + if(arg_TARGET_PATH) + string(REPLACE "/" "_" android_plugin_name_infix_name "${arg_TARGET_PATH}") + else() + string(REPLACE "." "_" android_plugin_name_infix_name "${arg_URI}") + endif() + + # If plugin supposed to use manually written qmldir file we don't prepend the uri-based + # prefix to the plugin output name. User should keep the file name of a QML plugin in + # qmldir the same as the name of plugin on a file system. Exception is the + # ABI-/platform-specific suffix that has the separate processing and should not be + # a part of plugin name in qmldir. + if(NOT no_generate_qmldir) + set(plugin_name + "qml_${android_plugin_name_infix_name}_${plugin_name}") + endif() + endif() + + set(${out_var} "${plugin_name}" PARENT_SCOPE) endfunction() # Used to add extra dependencies between ${target} and ${dep_target} qml plugins in a static diff --git a/src/qml/qml/qqmlpluginimporter.cpp b/src/qml/qml/qqmlpluginimporter.cpp index 512e08731e..c6d2fbe5c2 100644 --- a/src/qml/qml/qqmlpluginimporter.cpp +++ b/src/qml/qml/qqmlpluginimporter.cpp @@ -429,49 +429,59 @@ QString QQmlPluginImporter::resolvePlugin(const QString &qmldirPluginPath, const searchPaths.prepend(qmldirPluginPath); for (const QString &pluginPath : qAsConst(searchPaths)) { - QString resolvedPath; + QString resolvedBasePath; if (pluginPath == QLatin1String(".")) { if (qmldirPluginPathIsRelative && !qmldirPluginPath.isEmpty() && qmldirPluginPath != QLatin1String(".")) { - resolvedPath = QDir::cleanPath(qmldirPath + u'/' + qmldirPluginPath); + resolvedBasePath = QDir::cleanPath(qmldirPath + u'/' + qmldirPluginPath); } else { - resolvedPath = qmldirPath; + resolvedBasePath = qmldirPath; } } else { if (QDir::isRelativePath(pluginPath)) - resolvedPath = QDir::cleanPath(qmldirPath + u'/' + pluginPath); + resolvedBasePath = QDir::cleanPath(qmldirPath + u'/' + pluginPath); else - resolvedPath = pluginPath; + resolvedBasePath = pluginPath; } // hack for resources, should probably go away - if (resolvedPath.startsWith(u':')) - resolvedPath = QCoreApplication::applicationDirPath(); + if (resolvedBasePath.startsWith(u':')) + resolvedBasePath = QCoreApplication::applicationDirPath(); - if (!resolvedPath.endsWith(u'/')) - resolvedPath += u'/'; + if (!resolvedBasePath.endsWith(u'/')) + resolvedBasePath += u'/'; -#if defined(Q_OS_ANDROID) + QString resolvedPath = resolvedBasePath + prefix + baseName; + for (const QString &suffix : suffixes) { + const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix); + if (!absolutePath.isEmpty()) + return absolutePath; + } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) +# if defined(Q_OS_ANDROID) if (qmldirPath.size() > 25 && qmldirPath.at(0) == QLatin1Char(':') - && qmldirPath.at(1) == QLatin1Char('/') && - qmldirPath.startsWith(QStringLiteral(":/android_rcc_bundle/qml/"), - Qt::CaseInsensitive)) { + && qmldirPath.at(1) == QLatin1Char('/') + && qmldirPath.startsWith(QStringLiteral(":/android_rcc_bundle/qml/"), + Qt::CaseInsensitive)) { QString pluginName = qmldirPath.mid(21) + u'/' + baseName; pluginName.replace(QLatin1Char('/'), QLatin1Char('_')); - QString bundledPath = resolvedPath + QLatin1String("lib") + pluginName; + QString bundledPath = resolvedBasePath + QLatin1String("lib") + pluginName; for (const QString &suffix : suffixes) { const QString absolutePath = typeLoader->absoluteFilePath(bundledPath + suffix); - if (!absolutePath.isEmpty()) + if (!absolutePath.isEmpty()) { + qWarning("The implicit resolving of Qml plugin locations using the URI " + "embedded in the filename has been deprecated. Please use the " + "modern CMake API to create QML modules or set the name of " + "QML plugin in qmldir file, that matches the name of plugin " + "on file system. The correct plugin name is '%s'.", + qPrintable(pluginName)); return absolutePath; + } } } +# endif #endif - resolvedPath += prefix + baseName; - for (const QString &suffix : suffixes) { - const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix); - if (!absolutePath.isEmpty()) - return absolutePath; - } } qCDebug(lcQmlImport) << "resolvePlugin" << "Could not resolve plugin" |