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, {Plug & Paint Example}
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: QString()); // ugly, but we need a d-ptr
353 d->errorString.clear();
354 }
355 d->setLoadHints(loadHints);
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
370typedef QList<QStaticPlugin> StaticPluginList;
371Q_GLOBAL_STATIC(StaticPluginList, staticPluginList)
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(first: plugins.constBegin(), last: plugins.constEnd(), val: plugin, comp: comparator);
393 if (pos == plugins.constEnd() || pos->instance != plugin.instance)
394 plugins.insert(before: pos, t: 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(asize: 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"
469

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