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
5#include "qpluginloader.h"
6
7#include "qcoreapplication.h"
8#include "qdebug.h"
9#include "qdir.h"
10#include "qfactoryloader_p.h"
11#include "qfileinfo.h"
12#include "qjsondocument.h"
13
14#if QT_CONFIG(library)
15# include "qlibrary_p.h"
16#endif
17
18QT_BEGIN_NAMESPACE
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(s: "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(path: fileName);
226 if (isAbsolute) {
227 QFileInfo fi(fileName);
228 if (fi.isFile()) {
229 return fi.canonicalFilePath();
230 }
231 }
232 QStringList prefixes = QLibraryPrivate::prefixes_sys();
233 prefixes.prepend(t: QString());
234 QStringList suffixes = QLibraryPrivate::suffixes_sys(fullVersion: QString());
235 suffixes.prepend(t: QString());
236
237 // Split up "subdir/filename"
238 const qsizetype slash = fileName.lastIndexOf(c: u'/');
239 const auto baseName = QStringView{fileName}.mid(pos: slash + 1);
240 const auto basePath = isAbsolute ? QStringView() : QStringView{fileName}.left(n: slash + 1); // keep the '/'
241
242 QStringList paths;
243 if (isAbsolute) {
244 paths.append(t: fileName.left(n: slash)); // don't include the '/'
245 } else {
246 paths = QCoreApplication::libraryPaths();
247 }
248
249 for (const QString &path : std::as_const(t&: paths)) {
250 for (const QString &prefix : std::as_const(t&: prefixes)) {
251 for (const QString &suffix : std::as_const(t&: suffixes)) {
252#ifdef Q_OS_ANDROID
253 {
254 QString pluginPath = basePath + prefix + baseName + suffix;
255 const QString fn = path + "/lib"_L1 + pluginPath.replace(u'/', u'_');
256 qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
257 if (QFileInfo(fn).isFile())
258 return fn;
259 }
260#endif
261 const QString fn = path + u'/' + basePath + prefix + baseName + suffix;
262 qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
263 if (QFileInfo(fn).isFile())
264 return fn;
265 }
266 }
267 }
268 qCDebug(qt_lcDebugPlugins) << fileName << "not found";
269 return QString();
270}
271#endif
272
273/*!
274 \property QPluginLoader::fileName
275 \brief the file name of the plugin
276
277 We recommend omitting the file's suffix in the file name, since
278 QPluginLoader will automatically look for the file with the appropriate
279 suffix (see QLibrary::isLibrary()).
280
281 When loading the plugin, QPluginLoader searches
282 in all plugin locations specified by QCoreApplication::libraryPaths(),
283 unless the file name has an absolute path. After loading the plugin
284 successfully, fileName() returns the fully-qualified file name of
285 the plugin, including the full path to the plugin if one was given
286 in the constructor or passed to setFileName().
287
288 If the file name does not exist, it will not be set. This property
289 will then contain an empty string.
290
291 By default, this property contains an empty string.
292
293 \sa load()
294*/
295void QPluginLoader::setFileName(const QString &fileName)
296{
297#if defined(QT_SHARED)
298 QLibrary::LoadHints lh = defaultLoadHints;
299 if (d) {
300 lh = d->loadHints();
301 d->release();
302 d = nullptr;
303 did_load = false;
304 }
305
306 const QString fn = locatePlugin(fileName);
307
308 d = QLibraryPrivate::findOrCreate(fileName: fn, version: QString(), loadHints: lh);
309 if (!fn.isEmpty())
310 d->updatePluginState();
311
312#else
313 qCWarning(qt_lcDebugPlugins, "Cannot load '%ls' into a statically linked Qt library.",
314 qUtf16Printable(fileName));
315#endif
316}
317
318QString QPluginLoader::fileName() const
319{
320 if (d)
321 return d->fileName;
322 return QString();
323}
324
325/*!
326 \since 4.2
327
328 Returns a text string with the description of the last error that occurred.
329*/
330QString QPluginLoader::errorString() const
331{
332 return (!d || d->errorString.isEmpty()) ? tr(s: "Unknown error") : d->errorString;
333}
334
335/*! \since 4.4
336
337 \property QPluginLoader::loadHints
338 \brief Give the load() function some hints on how it should behave.
339
340 You can give hints on how the symbols in the plugin are
341 resolved. By default since Qt 5.7, QLibrary::PreventUnloadHint is set.
342
343 See the documentation of QLibrary::loadHints for a complete
344 description of how this property works.
345
346 \sa QLibrary::loadHints
347*/
348
349void QPluginLoader::setLoadHints(QLibrary::LoadHints loadHints)
350{
351 if (!d) {
352 d = QLibraryPrivate::findOrCreate(fileName: {}, version: {}, loadHints); // ugly, but we need a d-ptr
353 d->errorString.clear();
354 } else {
355 d->setLoadHints(loadHints);
356 }
357}
358
359QLibrary::LoadHints QPluginLoader::loadHints() const
360{
361 // Not having a d-pointer means that the user hasn't called
362 // setLoadHints() / setFileName() yet. In setFileName() we will
363 // then force defaultLoadHints on loading, so we must return them
364 // from here as well.
365
366 return d ? d->loadHints() : defaultLoadHints;
367}
368
369#endif // QT_CONFIG(library)
370
371typedef QList<QStaticPlugin> StaticPluginList;
372Q_GLOBAL_STATIC(StaticPluginList, staticPluginList)
373
374/*!
375 \relates QPluginLoader
376 \since 5.0
377
378 Registers the \a plugin specified with the plugin loader, and is used
379 by Q_IMPORT_PLUGIN().
380*/
381void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin)
382{
383 // using operator* because we shouldn't be registering plugins while
384 // unloading the application!
385 StaticPluginList &plugins = *staticPluginList;
386
387 // insert the plugin in the list, sorted by address, so we can detect
388 // duplicate registrations
389 auto comparator = [=](const QStaticPlugin &p1, const QStaticPlugin &p2) {
390 using Less = std::less<decltype(plugin.instance)>;
391 return Less{}(p1.instance, p2.instance);
392 };
393 auto pos = std::lower_bound(first: plugins.constBegin(), last: plugins.constEnd(), val: plugin, comp: comparator);
394 if (pos == plugins.constEnd() || pos->instance != plugin.instance)
395 plugins.insert(before: pos, t: plugin);
396}
397
398/*!
399 Returns a list of static plugin instances (root components) held
400 by the plugin loader.
401 \sa staticPlugins()
402*/
403QObjectList QPluginLoader::staticInstances()
404{
405 QObjectList instances;
406 if (staticPluginList.exists()) {
407 const StaticPluginList &plugins = *staticPluginList;
408 instances.reserve(asize: plugins.size());
409 for (QStaticPlugin plugin : plugins)
410 instances += plugin.instance();
411 }
412 return instances;
413}
414
415/*!
416 Returns a list of QStaticPlugins held by the plugin
417 loader. The function is similar to \l staticInstances()
418 with the addition that a QStaticPlugin also contains
419 meta data information.
420 \sa staticInstances()
421*/
422QList<QStaticPlugin> QPluginLoader::staticPlugins()
423{
424 StaticPluginList *plugins = staticPluginList();
425 if (plugins)
426 return *plugins;
427 return QList<QStaticPlugin>();
428}
429
430/*!
431 \class QStaticPlugin
432 \inmodule QtCore
433 \since 5.2
434
435 \brief QStaticPlugin is a struct containing a reference to a
436 static plugin instance together with its meta data.
437
438 \sa QPluginLoader, {How to Create Qt Plugins}
439*/
440
441/*!
442 \fn QStaticPlugin::QStaticPlugin(QtPluginInstanceFunction i, QtPluginMetaDataFunction m)
443 \internal
444*/
445
446/*!
447 \variable QStaticPlugin::instance
448
449 Holds the plugin instance.
450
451 \sa QPluginLoader::staticInstances()
452*/
453
454/*!
455 Returns a the meta data for the plugin as a QJsonObject.
456
457 \sa Q_PLUGIN_METADATA()
458*/
459QJsonObject QStaticPlugin::metaData() const
460{
461 QByteArrayView data(static_cast<const char *>(rawMetaData), rawMetaDataSize);
462 QPluginParsedMetaData parsed(data);
463 Q_ASSERT(!parsed.isError());
464 return parsed.toJson();
465}
466
467QT_END_NAMESPACE
468
469#include "moc_qpluginloader.cpp"
470

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/corelib/plugin/qpluginloader.cpp