Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qfactoryloader.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2022 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qfactoryloader_p.h"
6
7#ifndef QT_NO_QOBJECT
8#include "qfactoryinterface.h"
9
10#include "private/qcoreapplication_p.h"
11#include "private/qduplicatetracker_p.h"
12#include "private/qloggingregistry_p.h"
13#include "private/qobject_p.h"
14#include "qcborarray.h"
15#include "qcbormap.h"
16#include "qcborvalue.h"
17#include "qcborvalue.h"
18#include "qdiriterator.h"
19#include "qfileinfo.h"
20#include "qjsonarray.h"
21#include "qjsondocument.h"
22#include "qjsonobject.h"
23#include "qmap.h"
24#include "qmutex.h"
25#include "qplugin.h"
26#include "qplugin_p.h"
27#include "qpluginloader.h"
28
29#if QT_CONFIG(library)
30# include "qlibrary_p.h"
31#endif
32
33#include <qtcore_tracepoints_p.h>
34
36
37using namespace Qt::StringLiterals;
38
39Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName);
40
42{
44 Q_ASSERT(raw.size() >= qsizetype(sizeof(header)));
45 memcpy(&header, raw.data(), sizeof(header));
47 return setError(QFactoryLoader::tr("Invalid metadata version"));
48
49 // use fromRawData to keep QCborStreamReader from copying
50 raw = raw.sliced(sizeof(header));
53 QCborValue metadata = QCborValue::fromCbor(ba, &err);
54
55 if (err.error != QCborError::NoError)
56 return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(err.error.toString()));
57 if (!metadata.isMap())
58 return setError(QFactoryLoader::tr("Unexpected metadata contents"));
59 QCborMap map = metadata.toMap();
60 metadata = {};
61
62 DecodedArchRequirements archReq =
63 header.version == 0 ? decodeVersion0ArchRequirements(header.plugin_arch_requirements)
64 : decodeVersion1ArchRequirements(header.plugin_arch_requirements);
65
66 // insert the keys not stored in the top-level CBOR map
68 QT_VERSION_CHECK(header.qt_major_version, header.qt_minor_version, 0);
69 map[int(QtPluginMetaDataKeys::IsDebug)] = archReq.isDebug;
70 map[int(QtPluginMetaDataKeys::Requirements)] = archReq.level;
71
72 data = std::move(map);
73 return true;
74}
75
77{
78 // convert from the internal CBOR representation to an external JSON one
80 for (auto it : data.toMap()) {
82 if (it.first.isInteger()) {
83 switch (it.first.toInteger()) {
84#define CONVERT_TO_STRING(IntKey, StringKey, Description) \
85 case int(IntKey): key = QStringLiteral(StringKey); break;
87 }
88 } else {
89 key = it.first.toString();
90 }
91
92 if (!key.isEmpty())
93 o.insert(key, it.second.toJsonValue());
94 }
95 return o;
96}
97
99{
100 Q_DECLARE_PUBLIC(QFactoryLoader)
101public:
104#if QT_CONFIG(library)
106 mutable QMutex mutex;
107 QDuplicateTracker<QString> loadedPaths;
108 QList<QLibraryPrivate*> libraryList;
110 QString suffix;
111 QString extraSearchPath;
113
114 void updateSinglePath(const QString &pluginDir);
115#endif
116};
117
118#if QT_CONFIG(library)
119
120static Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcFactoryLoader, "QT_DEBUG_PLUGINS",
121 "qt.core.plugin.factoryloader")
122
123namespace {
124struct QFactoryLoaderGlobals
125{
126 // needs to be recursive because loading one plugin could cause another
127 // factory to be initialized
130};
131}
132
133Q_GLOBAL_STATIC(QFactoryLoaderGlobals, qt_factoryloader_global)
134
135QFactoryLoaderPrivate::~QFactoryLoaderPrivate()
136{
137 for (QLibraryPrivate *library : std::as_const(libraryList))
138 library->release();
139}
140
141inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
142{
143 struct LibraryReleaser {
144 void operator()(QLibraryPrivate *library)
145 { if (library) library->release(); }
146 };
147
148 // If we've already loaded, skip it...
149 if (loadedPaths.hasSeen(path))
150 return;
151
152 qCDebug(lcFactoryLoader) << "checking directory path" << path << "...";
153
154 QDirIterator plugins(path,
155#if defined(Q_OS_WIN)
156 QStringList(QStringLiteral("*.dll")),
157#elif defined(Q_OS_ANDROID)
158 QStringList("libplugins_%1_*.so"_L1.arg(suffix)),
159#endif
161
162 while (plugins.hasNext()) {
163 QString fileName = plugins.next();
164#ifdef Q_OS_DARWIN
165 const bool isDebugPlugin = fileName.endsWith("_debug.dylib"_L1);
166 const bool isDebugLibrary =
167 #ifdef QT_DEBUG
168 true;
169 #else
170 false;
171 #endif
172
173 // Skip mismatching plugins so that we don't end up loading both debug and release
174 // versions of the same Qt libraries (due to the plugin's dependencies).
175 if (isDebugPlugin != isDebugLibrary)
176 continue;
177#elif defined(Q_PROCESSOR_X86)
178 if (fileName.endsWith(".avx2"_L1) || fileName.endsWith(".avx512"_L1)) {
179 // ignore AVX2-optimized file, we'll do a bait-and-switch to it later
180 continue;
181 }
182#endif
183 qCDebug(lcFactoryLoader) << "looking at" << fileName;
184
185 Q_TRACE(QFactoryLoader_update, fileName);
186
187 std::unique_ptr<QLibraryPrivate, LibraryReleaser> library;
188 library.reset(QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath()));
189 if (!library->isPlugin()) {
190 qCDebug(lcFactoryLoader) << library->errorString << Qt::endl
191 << " not a plugin";
192 continue;
193 }
194
196 bool metaDataOk = false;
197
198 QString iid = library->metaData.value(QtPluginMetaDataKeys::IID).toString();
199 if (iid == QLatin1StringView(this->iid.constData(), this->iid.size())) {
200 QCborMap object = library->metaData.value(QtPluginMetaDataKeys::MetaData).toMap();
201 metaDataOk = true;
202
203 const QCborArray k = object.value("Keys"_L1).toArray();
204 for (QCborValueConstRef v : k)
205 keys += cs ? v.toString() : v.toString().toLower();
206 }
207 qCDebug(lcFactoryLoader) << "Got keys from plugin meta data" << keys;
208
209 if (!metaDataOk)
210 continue;
211
212 int keyUsageCount = 0;
213 for (const QString &key : std::as_const(keys)) {
214 // first come first serve, unless the first
215 // library was built with a future Qt version,
216 // whereas the new one has a Qt version that fits
217 // better
218 constexpr int QtVersionNoPatch = QT_VERSION_CHECK(QT_VERSION_MAJOR, QT_VERSION_MINOR, 0);
219 QLibraryPrivate *previous = keyMap.value(key);
220 int prev_qt_version = 0;
221 if (previous)
222 prev_qt_version = int(previous->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger());
223 int qt_version = int(library->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger());
224 if (!previous || (prev_qt_version > QtVersionNoPatch && qt_version <= QtVersionNoPatch)) {
225 keyMap[key] = library.get(); // we WILL .release()
226 ++keyUsageCount;
227 }
228 }
229 if (keyUsageCount || keys.isEmpty()) {
230 library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload
231 QMutexLocker locker(&mutex);
232 libraryList += library.release();
233 }
234 };
235}
236
237void QFactoryLoader::update()
238{
239#ifdef QT_SHARED
240 Q_D(QFactoryLoader);
241
242 const QStringList paths = QCoreApplication::libraryPaths();
243 for (const QString &pluginDir : paths) {
244#ifdef Q_OS_ANDROID
245 QString path = pluginDir;
246#else
247 QString path = pluginDir + d->suffix;
248#endif
249
250 d->updateSinglePath(path);
251 }
252 if (!d->extraSearchPath.isEmpty())
253 d->updateSinglePath(d->extraSearchPath);
254#else
255 Q_D(QFactoryLoader);
256 qCDebug(lcFactoryLoader) << "ignoring" << d->iid
257 << "since plugins are disabled in static builds";
258#endif
259}
260
261QFactoryLoader::~QFactoryLoader()
262{
263 if (!qt_factoryloader_global.isDestroyed()) {
264 QMutexLocker locker(&qt_factoryloader_global->mutex);
265 qt_factoryloader_global->loaders.removeOne(this);
266 }
267}
268
269#if defined(Q_OS_UNIX) && !defined (Q_OS_DARWIN)
270QLibraryPrivate *QFactoryLoader::library(const QString &key) const
271{
272 Q_D(const QFactoryLoader);
273 return d->keyMap.value(d->cs ? key : key.toLower());
274}
275#endif
276
277void QFactoryLoader::refreshAll()
278{
279 if (qt_factoryloader_global.exists()) {
280 QMutexLocker locker(&qt_factoryloader_global->mutex);
281 for (QFactoryLoader *loader : std::as_const(qt_factoryloader_global->loaders))
282 loader->update();
283 }
284}
285
286#endif // QT_CONFIG(library)
287
289 const QString &suffix,
292{
293 Q_ASSERT_X(suffix.startsWith(u'/'), "QFactoryLoader",
294 "For historical reasons, the suffix must start with '/' (and it can't be empty)");
295
297 Q_D(QFactoryLoader);
298 d->iid = iid;
299#if QT_CONFIG(library)
300 d->cs = cs;
301 d->suffix = suffix;
302# ifdef Q_OS_ANDROID
303 if (!d->suffix.isEmpty() && d->suffix.at(0) == u'/')
304 d->suffix.remove(0, 1);
305# endif
306
307 QMutexLocker locker(&qt_factoryloader_global->mutex);
308 update();
309 qt_factoryloader_global->loaders.append(this);
310#else
311 Q_UNUSED(suffix);
312 Q_UNUSED(cs);
313#endif
314}
315
317{
318#if QT_CONFIG(library)
319 Q_D(QFactoryLoader);
320 if (d->extraSearchPath == path)
321 return; // nothing to do
322
323 QMutexLocker locker(&qt_factoryloader_global->mutex);
324 QString oldPath = std::exchange(d->extraSearchPath, path);
325 if (oldPath.isEmpty()) {
326 // easy case, just update this directory
327 d->updateSinglePath(d->extraSearchPath);
328 } else {
329 // must re-scan everything
330 d->loadedPaths.clear();
331 d->libraryList.clear();
332 d->keyMap.clear();
333 update();
334 }
335#else
336 Q_UNUSED(path);
337#endif
338}
339
341{
342 Q_D(const QFactoryLoader);
344#if QT_CONFIG(library)
345 QMutexLocker locker(&d->mutex);
346 for (QLibraryPrivate *library : std::as_const(d->libraryList))
347 metaData.append(library->metaData);
348#endif
349
350 QLatin1StringView iid(d->iid.constData(), d->iid.size());
351 const auto staticPlugins = QPluginLoader::staticPlugins();
352 for (const QStaticPlugin &plugin : staticPlugins) {
353 QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize);
354 QPluginParsedMetaData parsed(pluginData);
355 if (parsed.isError() || parsed.value(QtPluginMetaDataKeys::IID) != iid)
356 continue;
357 metaData.append(std::move(parsed));
358 }
359
360 Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max());
361 return metaData;
362}
363
365{
366 Q_D(const QFactoryLoader);
367 if (index < 0)
368 return nullptr;
369
370#if QT_CONFIG(library)
371 QMutexLocker lock(&d->mutex);
372 if (index < d->libraryList.size()) {
373 QLibraryPrivate *library = d->libraryList.at(index);
374 if (QObject *obj = library->pluginInstance()) {
375 if (!obj->parent())
377 return obj;
378 }
379 return nullptr;
380 }
381 index -= d->libraryList.size();
382 lock.unlock();
383#endif
384
385 QLatin1StringView iid(d->iid.constData(), d->iid.size());
387 for (QStaticPlugin plugin : staticPlugins) {
388 QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize);
389 QPluginParsedMetaData parsed(pluginData);
390 if (parsed.isError() || parsed.value(QtPluginMetaDataKeys::IID) != iid)
391 continue;
392
393 if (index == 0)
394 return plugin.instance();
395 --index;
396 }
397
398 return nullptr;
399}
400
402{
404 const QList<QPluginParsedMetaData> metaDataList = metaData();
405 for (int i = 0; i < int(metaDataList.size()); ++i) {
406 const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap();
407 const QCborArray keys = metaData.value("Keys"_L1).toArray();
409 result.insert(i, key.toString());
410 }
411 return result;
412}
413
414int QFactoryLoader::indexOf(const QString &needle) const
415{
416 const QList<QPluginParsedMetaData> metaDataList = metaData();
417 for (int i = 0; i < int(metaDataList.size()); ++i) {
418 const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap();
419 const QCborArray keys = metaData.value("Keys"_L1).toArray();
420 for (QCborValueConstRef key : keys) {
421 if (key.toString().compare(needle, Qt::CaseInsensitive) == 0)
422 return i;
423 }
424 }
425 return -1;
426}
427
429
430#include "moc_qfactoryloader_p.cpp"
431
432#endif // QT_NO_QOBJECT
constexpr QByteArrayView sliced(qsizetype pos) const
constexpr qsizetype size() const noexcept
constexpr const_pointer data() const noexcept
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:394
\inmodule QtCore\reentrant
Definition qcborarray.h:20
\inmodule QtCore\reentrant
Definition qcbormap.h:21
QCborValue value(qint64 key) const
Returns the QCborValue element in this map that corresponds to key key, if there is one.
Definition qcbormap.h:169
\inmodule QtCore\reentrant
Definition qcborvalue.h:50
QCborMap toMap() const
qint64 toInteger(qint64 defaultValue=0) const
Returns the integer value stored in this QCborValue, if it is of the integer type.
Definition qcborvalue.h:190
bool isMap() const
Returns true if this QCborValue is of the map type.
Definition qcborvalue.h:162
static QThread * mainThread()
The QDirIterator class provides an iterator for directory entrylists.
@ Files
Definition qdir.h:22
void setExtraSearchPath(const QString &path)
int indexOf(const QString &needle) const
QMultiMap< int, QString > keyMap() const
QFactoryLoader(const char *iid, const QString &suffix=QString(), Qt::CaseSensitivity=Qt::CaseSensitive)
MetaDataList metaData() const
QObject * instance(int index) const
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
QPluginParsedMetaData metaData
Definition qlibrary_p.h:82
QObject * pluginInstance()
Definition qlibrary.cpp:492
static QLibraryPrivate * findOrCreate(const QString &fileName, const QString &version=QString(), QLibrary::LoadHints loadHints={ })
Definition qlibrary.cpp:452
@ PreventUnloadHint
Definition qlibrary.h:26
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
T value(qsizetype i) const
Definition qlist.h:661
void append(parameter_type t)
Definition qlist.h:441
Definition qmap.h:186
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
\inmodule QtCore
Definition qobject.h:90
void moveToThread(QThread *thread)
Changes the thread affinity for this object and its children.
Definition qobject.cpp:1606
static QList< QStaticPlugin > staticPlugins()
Returns a list of QStaticPlugins held by the plugin loader.
QCborValue value(QtPluginMetaDataKeys k) const
bool parse(QByteArrayView input)
QJsonObject toJson() const
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
#define this
Definition dialogs.cpp:9
QMap< QString, QString > map
[6]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
CaseSensitivity
@ CaseInsensitive
QTextStream & endl(QTextStream &stream)
Writes '\n' to the stream and flushes the stream.
#define Q_UNLIKELY(x)
static QString header(const QString &name)
#define CONVERT_TO_STRING(IntKey, StringKey, Description)
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qCDebug(category,...)
#define Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(name, env, categoryName)
GLsizei const GLfloat * v
[13]
GLuint64 key
GLuint index
[2]
GLsizei const GLuint * paths
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLhandleARB obj
[2]
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
#define QT_PLUGIN_FOREACH_METADATA(F)
Definition qplugin_p.h:34
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
SSL_CTX int(*) void arg)
#define QStringLiteral(str)
#define Q_UNUSED(x)
#define Q_TRACE(x,...)
Definition qtrace_p.h:144
#define Q_TRACE_POINT(provider, tracepoint,...)
Definition qtrace_p.h:232
#define QT_VERSION_CHECK(major, minor, patch)
ptrdiff_t qsizetype
Definition qtypes.h:70
static const struct @437 keyMap[]
QByteArray ba
[0]
QStringList keys
sem release()
QMutex mutex
[2]
QReadWriteLock lock
[0]
char * toString(const MyType &t)
[31]
QString toString() const
Returns a text string that matches the error code in this QCborError object.
\inmodule QtCore\reentrant
Definition qcborvalue.h:40
QCborError error
Definition qcborvalue.h:42
static constexpr quint8 CurrentMetaDataVersion
Definition qplugin.h:39
\inmodule QtCore
Definition qplugin.h:110