QFactoryLoader: Prioritize plugin that matches Qt's build config

In debug-and-release builds (on Windows or Apple operating systems),
we will scan for plugin libraries and find both debug and release
versions of these libraries.

However, we do not end up loading both, thanks to additional logic.
On Windows we rely on logic in QLibraryPrivate::updatePluginState()
that uses the IsDebug metadata of the plugin, skipping any mismatch.

On Apple operating systems, there is logic in QFactoryLoaderPrivate's
updateSinglePath that ties to match the `_debug` suffix of the plugin
with the Qt build config.

As the string matching logic for Apple platforms is fragile we want
to remove it, but we can't re-use the logic for Windows, to ensure
we only load a single copy of a plugin, as the Windows logic prevents
_any_ mismatch between plugin config and Qt config, even for non-
debug-and-release builds (due to the Windows runtime being incompatible
between debug and release).

To solve this we add logic to QFactoryLoader to prioritize plugins
based on the Qt build config, similar to how we prioritize plugins
based on their Qt version if we find two or more plugins claiming
the same plugin key.

Change-Id: I772ba8ae79627e39418ba80107e3729bba8f9ac8
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 90773b501795a088762dd540c442a7cd251a5d8e)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Tor Arne Vestbø 2024-06-20 17:15:14 +02:00 committed by Qt Cherry-pick Bot
parent 1c66b77f52
commit 67b3c40ae2

View File

@ -363,20 +363,28 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
int keyUsageCount = 0;
for (const QString &key : std::as_const(keys)) {
// first come first serve, unless the first
// library was built with a future Qt version,
// whereas the new one has a Qt version that fits
// better
constexpr int QtVersionNoPatch = QT_VERSION_CHECK(QT_VERSION_MAJOR, QT_VERSION_MINOR, 0);
QLibraryPrivate *&keyMapEntry = keyMap[key];
int prev_qt_version = 0;
if (keyMapEntry)
prev_qt_version = int(keyMapEntry->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger());
int qt_version = int(library->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger());
if (!keyMapEntry || (prev_qt_version > QtVersionNoPatch && qt_version <= QtVersionNoPatch)) {
keyMapEntry = library.get(); // we WILL .release()
++keyUsageCount;
if (QLibraryPrivate *existingLibrary = keyMapEntry) {
static constexpr bool QtBuildIsDebug = QT_CONFIG(debug);
bool existingIsDebug = existingLibrary->metaData.value(QtPluginMetaDataKeys::IsDebug).toBool();
bool thisIsDebug = library->metaData.value(QtPluginMetaDataKeys::IsDebug).toBool();
bool configsAreDifferent = thisIsDebug != existingIsDebug;
bool thisConfigDoesNotMatchQt = thisIsDebug != QtBuildIsDebug;
if (configsAreDifferent && thisConfigDoesNotMatchQt)
continue; // Existing library matches Qt's build config
// If the existing library was built with a future Qt version,
// whereas the one we're considering has a Qt version that fits
// better, we prioritize the better match.
static constexpr qint64 QtVersionNoPatch = QT_VERSION_CHECK(QT_VERSION_MAJOR, QT_VERSION_MINOR, 0);
int existingVersion = existingLibrary->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger();
int thisVersion = library->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger();
if (!(existingVersion > QtVersionNoPatch && thisVersion <= QtVersionNoPatch))
continue; // Existing version is a better match
}
keyMapEntry = library.get();
++keyUsageCount;
}
if (keyUsageCount || keys.isEmpty()) {
library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload