Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpluginloader.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2018 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
6
8#include "qdebug.h"
9#include "qdir.h"
11#include "qfileinfo.h"
12#include "qjsondocument.h"
13
14#if QT_CONFIG(library)
15# include "qlibrary_p.h"
16#endif
17
19
20using namespace Qt::StringLiterals;
21
22#if QT_CONFIG(library)
23
24/*!
25 \class QPluginLoader
26 \inmodule QtCore
27 \reentrant
28 \brief The QPluginLoader class loads a plugin at run-time.
29
30
31 \ingroup plugins
32
33 QPluginLoader provides access to a \l{How to Create Qt
34 Plugins}{Qt plugin}. A Qt plugin is stored in a shared library (a
35 DLL) and offers these benefits over shared libraries accessed
36 using QLibrary:
37
38 \list
39 \li QPluginLoader checks that a plugin is linked against the same
40 version of Qt as the application.
41 \li QPluginLoader provides direct access to a root component object
42 (instance()), instead of forcing you to resolve a C function manually.
43 \endlist
44
45 An instance of a QPluginLoader object operates on a single shared
46 library file, which we call a plugin. It provides access to the
47 functionality in the plugin in a platform-independent way. To
48 specify which plugin to load, either pass a file name in
49 the constructor or set it with setFileName().
50
51 The most important functions are load() to dynamically load the
52 plugin file, isLoaded() to check whether loading was successful,
53 and instance() to access the root component in the plugin. The
54 instance() function implicitly tries to load the plugin if it has
55 not been loaded yet. Multiple instances of QPluginLoader can be
56 used to access the same physical plugin.
57
58 Once loaded, plugins remain in memory until all instances of
59 QPluginLoader has been unloaded, or until the application
60 terminates. You can attempt to unload a plugin using unload(),
61 but if other instances of QPluginLoader are using the same
62 library, the call will fail, and unloading will only happen when
63 every instance has called unload(). Right before the unloading
64 happens, the root component will also be deleted.
65
66 See \l{How to Create Qt Plugins} for more information about
67 how to make your application extensible through plugins.
68
69 Note that the QPluginLoader cannot be used if your application is
70 statically linked against Qt. In this case, you will also have to
71 link to plugins statically. You can use QLibrary if you need to
72 load dynamic libraries in a statically linked application.
73
74 \sa QLibrary
75*/
76
77static constexpr QLibrary::LoadHints defaultLoadHints = QLibrary::PreventUnloadHint;
78
79/*!
80 Constructs a plugin loader with the given \a parent.
81*/
82QPluginLoader::QPluginLoader(QObject *parent)
83 : QObject(parent), d(nullptr), did_load(false)
84{
85}
86
87/*!
88 Constructs a plugin loader with the given \a parent that will
89 load the plugin specified by \a fileName.
90
91 To be loadable, the file's suffix must be a valid suffix for a
92 loadable library in accordance with the platform, e.g. \c .so on
93 Unix, - \c .dylib on \macos and iOS, and \c .dll on Windows. The suffix
94 can be verified with QLibrary::isLibrary().
95
96 \sa setFileName()
97*/
98QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent)
99 : QObject(parent), d(nullptr), did_load(false)
100{
101 setFileName(fileName);
102 setLoadHints(defaultLoadHints);
103}
104
105/*!
106 Destroys the QPluginLoader object.
107
108 Unless unload() was called explicitly, the plugin stays in memory
109 until the application terminates.
110
111 \sa isLoaded(), unload()
112*/
113QPluginLoader::~QPluginLoader()
114{
115 if (d)
116 d->release();
117}
118
119/*!
120 Returns the root component object of the plugin. The plugin is
121 loaded if necessary. The function returns \nullptr if the plugin could
122 not be loaded or if the root component object could not be
123 instantiated.
124
125 If the root component object was destroyed, calling this function
126 creates a new instance.
127
128 The root component, returned by this function, is not deleted when
129 the QPluginLoader is destroyed. If you want to ensure that the root
130 component is deleted, you should call unload() as soon you don't
131 need to access the core component anymore. When the library is
132 finally unloaded, the root component will automatically be deleted.
133
134 The component object is a QObject. Use qobject_cast() to access
135 interfaces you are interested in.
136
137 \sa load()
138*/
139QObject *QPluginLoader::instance()
140{
141 if (!isLoaded() && !load())
142 return nullptr;
143 return d->pluginInstance();
144}
145
146/*!
147 Returns the meta data for this plugin. The meta data is data specified
148 in a json format using the Q_PLUGIN_METADATA() macro when compiling
149 the plugin.
150
151 The meta data can be queried in a fast and inexpensive way without
152 actually loading the plugin. This makes it possible to e.g. store
153 capabilities of the plugin in there, and make the decision whether to
154 load the plugin dependent on this meta data.
155 */
156QJsonObject QPluginLoader::metaData() const
157{
158 if (!d)
159 return QJsonObject();
160 return d->metaData.toJson();
161}
162
163/*!
164 Loads the plugin and returns \c true if the plugin was loaded
165 successfully; otherwise returns \c false. Since instance() always
166 calls this function before resolving any symbols it is not
167 necessary to call it explicitly. In some situations you might want
168 the plugin loaded in advance, in which case you would use this
169 function.
170
171 \sa unload()
172*/
173bool QPluginLoader::load()
174{
175 if (!d || d->fileName.isEmpty())
176 return false;
177 if (did_load)
178 return d->pHnd && d->instanceFactory.loadAcquire();
179 if (!d->isPlugin())
180 return false;
181 did_load = true;
182 return d->loadPlugin();
183}
184
185/*!
186 Unloads the plugin and returns \c true if the plugin could be
187 unloaded; otherwise returns \c false.
188
189 This happens automatically on application termination, so you
190 shouldn't normally need to call this function.
191
192 If other instances of QPluginLoader are using the same plugin, the
193 call will fail, and unloading will only happen when every instance
194 has called unload().
195
196 Don't try to delete the root component. Instead rely on
197 that unload() will automatically delete it when needed.
198
199 \sa instance(), load()
200*/
201bool QPluginLoader::unload()
202{
203 if (did_load) {
204 did_load = false;
205 return d->unload();
206 }
207 if (d) // Ouch
208 d->errorString = tr("The plugin was not loaded.");
209 return false;
210}
211
212/*!
213 Returns \c true if the plugin is loaded; otherwise returns \c false.
214
215 \sa load()
216 */
217bool QPluginLoader::isLoaded() const
218{
219 return d && d->pHnd && d->instanceFactory.loadRelaxed();
220}
221
222#if defined(QT_SHARED)
223static QString locatePlugin(const QString& fileName)
224{
225 const bool isAbsolute = QDir::isAbsolutePath(fileName);
226 if (isAbsolute) {
227 QFileInfo fi(fileName);
228 if (fi.isFile()) {
229 return fi.canonicalFilePath();
230 }
231 }
232 std::array<QStringView, 2> prefixes = { QStringView(), QLibraryPrivate::prefix_sys() };
233 QStringList suffixes = QLibraryPrivate::suffixes_sys(QString());
234 suffixes.prepend(QString());
235
236 // Split up "subdir/filename"
237 const qsizetype slash = fileName.lastIndexOf(u'/');
238 const auto baseName = QStringView{fileName}.mid(slash + 1);
239 const auto basePath = isAbsolute ? QStringView() : QStringView{fileName}.left(slash + 1); // keep the '/'
240
241 QStringList paths;
242 if (isAbsolute) {
243 paths.append(fileName.left(slash)); // don't include the '/'
244 } else {
245 paths = QCoreApplication::libraryPaths();
246 }
247
248 for (const QString &path : std::as_const(paths)) {
249 for (QStringView prefix : prefixes) {
250 for (const QString &suffix : std::as_const(suffixes)) {
251#ifdef Q_OS_ANDROID
252 {
253 QString pluginPath = basePath + prefix + baseName + suffix;
254 const QString fn = path + "/lib"_L1 + pluginPath.replace(u'/', u'_');
255 qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
256 if (QFileInfo(fn).isFile())
257 return fn;
258 }
259#endif
260 const QString fn = path + u'/' + basePath + prefix + baseName + suffix;
261 qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
262 if (QFileInfo(fn).isFile())
263 return fn;
264 }
265 }
266 }
267 qCDebug(qt_lcDebugPlugins) << fileName << "not found";
268 return QString();
269}
270#endif
271
272/*!
273 \property QPluginLoader::fileName
274 \brief the file name of the plugin
275
276 We recommend omitting the file's suffix in the file name, since
277 QPluginLoader will automatically look for the file with the appropriate
278 suffix (see QLibrary::isLibrary()).
279
280 When loading the plugin, QPluginLoader searches
281 in all plugin locations specified by QCoreApplication::libraryPaths(),
282 unless the file name has an absolute path. After loading the plugin
283 successfully, fileName() returns the fully-qualified file name of
284 the plugin, including the full path to the plugin if one was given
285 in the constructor or passed to setFileName().
286
287 If the file name does not exist, it will not be set. This property
288 will then contain an empty string.
289
290 By default, this property contains an empty string.
291
292 \sa load()
293*/
294void QPluginLoader::setFileName(const QString &fileName)
295{
296#if defined(QT_SHARED)
297 QLibrary::LoadHints lh = defaultLoadHints;
298 if (d) {
299 lh = d->loadHints();
300 d->release();
301 d = nullptr;
302 did_load = false;
303 }
304
305 const QString fn = locatePlugin(fileName);
306
307 d = QLibraryPrivate::findOrCreate(fn, QString(), lh);
308 if (!fn.isEmpty())
309 d->updatePluginState();
310
311#else
312 qCWarning(qt_lcDebugPlugins, "Cannot load '%ls' into a statically linked Qt library.",
313 qUtf16Printable(fileName));
314#endif
315}
316
317QString QPluginLoader::fileName() const
318{
319 if (d)
320 return d->fileName;
321 return QString();
322}
323
324/*!
325 \since 4.2
326
327 Returns a text string with the description of the last error that occurred.
328*/
329QString QPluginLoader::errorString() const
330{
331 return (!d || d->errorString.isEmpty()) ? tr("Unknown error") : d->errorString;
332}
333
334/*! \since 4.4
335
336 \property QPluginLoader::loadHints
337 \brief Give the load() function some hints on how it should behave.
338
339 You can give hints on how the symbols in the plugin are
340 resolved. By default since Qt 5.7, QLibrary::PreventUnloadHint is set.
341
342 See the documentation of QLibrary::loadHints for a complete
343 description of how this property works.
344
345 \sa QLibrary::loadHints
346*/
347
348void QPluginLoader::setLoadHints(QLibrary::LoadHints loadHints)
349{
350 if (!d) {
351 d = QLibraryPrivate::findOrCreate({}, {}, loadHints); // ugly, but we need a d-ptr
352 d->errorString.clear();
353 } else {
354 d->setLoadHints(loadHints);
355 }
356}
357
358QLibrary::LoadHints QPluginLoader::loadHints() const
359{
360 // Not having a d-pointer means that the user hasn't called
361 // setLoadHints() / setFileName() yet. In setFileName() we will
362 // then force defaultLoadHints on loading, so we must return them
363 // from here as well.
364
365 return d ? d->loadHints() : defaultLoadHints;
366}
367
368#endif // QT_CONFIG(library)
369
372
373/*!
374 \relates QPluginLoader
375 \since 5.0
376
377 Registers the \a plugin specified with the plugin loader, and is used
378 by Q_IMPORT_PLUGIN().
379*/
380void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin)
381{
382 // using operator* because we shouldn't be registering plugins while
383 // unloading the application!
384 StaticPluginList &plugins = *staticPluginList;
385
386 // insert the plugin in the list, sorted by address, so we can detect
387 // duplicate registrations
388 auto comparator = [=](const QStaticPlugin &p1, const QStaticPlugin &p2) {
389 using Less = std::less<decltype(plugin.instance)>;
390 return Less{}(p1.instance, p2.instance);
391 };
392 auto pos = std::lower_bound(plugins.constBegin(), plugins.constEnd(), plugin, comparator);
393 if (pos == plugins.constEnd() || pos->instance != plugin.instance)
394 plugins.insert(pos, plugin);
395}
396
397/*!
398 Returns a list of static plugin instances (root components) held
399 by the plugin loader.
400 \sa staticPlugins()
401*/
402QObjectList QPluginLoader::staticInstances()
403{
404 QObjectList instances;
405 if (staticPluginList.exists()) {
406 const StaticPluginList &plugins = *staticPluginList;
407 instances.reserve(plugins.size());
408 for (QStaticPlugin plugin : plugins)
409 instances += plugin.instance();
410 }
411 return instances;
412}
413
414/*!
415 Returns a list of QStaticPlugins held by the plugin
416 loader. The function is similar to \l staticInstances()
417 with the addition that a QStaticPlugin also contains
418 meta data information.
419 \sa staticInstances()
420*/
421QList<QStaticPlugin> QPluginLoader::staticPlugins()
422{
423 StaticPluginList *plugins = staticPluginList();
424 if (plugins)
425 return *plugins;
426 return QList<QStaticPlugin>();
427}
428
429/*!
430 \class QStaticPlugin
431 \inmodule QtCore
432 \since 5.2
433
434 \brief QStaticPlugin is a struct containing a reference to a
435 static plugin instance together with its meta data.
436
437 \sa QPluginLoader, {How to Create Qt Plugins}
438*/
439
440/*!
441 \fn QStaticPlugin::QStaticPlugin(QtPluginInstanceFunction i, QtPluginMetaDataFunction m)
442 \internal
443*/
444
445/*!
446 \variable QStaticPlugin::instance
447
448 Holds the plugin instance.
449
450 \sa QPluginLoader::staticInstances()
451*/
452
453/*!
454 Returns a the meta data for the plugin as a QJsonObject.
455
456 \sa Q_PLUGIN_METADATA()
457*/
458QJsonObject QStaticPlugin::metaData() const
459{
460 QByteArrayView data(static_cast<const char *>(rawMetaData), rawMetaDataSize);
461 QPluginParsedMetaData parsed(data);
462 Q_ASSERT(!parsed.isError());
463 return parsed.toJson();
464}
465
466QT_END_NAMESPACE
467
468#include "moc_qpluginloader.cpp"
Combined button and popup list for selecting options.
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
QList< QStaticPlugin > StaticPluginList