1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2018 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qplatformdefs.h"
42
43#include "qplugin.h"
44#include "qcoreapplication.h"
45#include "qpluginloader.h"
46#include <qfileinfo.h>
47#include "qfactoryloader_p.h"
48#include "qdebug.h"
49#include "qdir.h"
50
51QT_BEGIN_NAMESPACE
52
53#if QT_CONFIG(library)
54
55/*!
56 \class QPluginLoader
57 \inmodule QtCore
58 \reentrant
59 \brief The QPluginLoader class loads a plugin at run-time.
60
61
62 \ingroup plugins
63
64 QPluginLoader provides access to a \l{How to Create Qt
65 Plugins}{Qt plugin}. A Qt plugin is stored in a shared library (a
66 DLL) and offers these benefits over shared libraries accessed
67 using QLibrary:
68
69 \list
70 \li QPluginLoader checks that a plugin is linked against the same
71 version of Qt as the application.
72 \li QPluginLoader provides direct access to a root component object
73 (instance()), instead of forcing you to resolve a C function manually.
74 \endlist
75
76 An instance of a QPluginLoader object operates on a single shared
77 library file, which we call a plugin. It provides access to the
78 functionality in the plugin in a platform-independent way. To
79 specify which plugin to load, either pass a file name in
80 the constructor or set it with setFileName().
81
82 The most important functions are load() to dynamically load the
83 plugin file, isLoaded() to check whether loading was successful,
84 and instance() to access the root component in the plugin. The
85 instance() function implicitly tries to load the plugin if it has
86 not been loaded yet. Multiple instances of QPluginLoader can be
87 used to access the same physical plugin.
88
89 Once loaded, plugins remain in memory until all instances of
90 QPluginLoader has been unloaded, or until the application
91 terminates. You can attempt to unload a plugin using unload(),
92 but if other instances of QPluginLoader are using the same
93 library, the call will fail, and unloading will only happen when
94 every instance has called unload(). Right before the unloading
95 happens, the root component will also be deleted.
96
97 See \l{How to Create Qt Plugins} for more information about
98 how to make your application extensible through plugins.
99
100 Note that the QPluginLoader cannot be used if your application is
101 statically linked against Qt. In this case, you will also have to
102 link to plugins statically. You can use QLibrary if you need to
103 load dynamic libraries in a statically linked application.
104
105 \sa QLibrary, {Plug & Paint Example}
106*/
107
108/*!
109 \class QStaticPlugin
110 \inmodule QtCore
111 \since 5.2
112
113 \brief QStaticPlugin is a struct containing a reference to a
114 static plugin instance together with its meta data.
115
116 \sa QPluginLoader, {How to Create Qt Plugins}
117*/
118
119/*!
120 \fn QObject *QStaticPlugin::instance()
121
122 Returns the plugin instance.
123
124 \sa QPluginLoader::staticInstances()
125*/
126
127/*!
128 \fn const char *QStaticPlugin::rawMetaData()
129
130 Returns the raw meta data for the plugin.
131
132 \sa metaData(), Q_PLUGIN_METADATA()
133*/
134
135/*!
136 Constructs a plugin loader with the given \a parent.
137*/
138QPluginLoader::QPluginLoader(QObject *parent)
139 : QObject(parent), d(nullptr), did_load(false)
140{
141}
142
143/*!
144 Constructs a plugin loader with the given \a parent that will
145 load the plugin specified by \a fileName.
146
147 To be loadable, the file's suffix must be a valid suffix for a
148 loadable library in accordance with the platform, e.g. \c .so on
149 Unix, - \c .dylib on \macos and iOS, and \c .dll on Windows. The suffix
150 can be verified with QLibrary::isLibrary().
151
152 \sa setFileName()
153*/
154QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent)
155 : QObject(parent), d(nullptr), did_load(false)
156{
157 setFileName(fileName);
158 setLoadHints(QLibrary::PreventUnloadHint);
159}
160
161/*!
162 Destroys the QPluginLoader object.
163
164 Unless unload() was called explicitly, the plugin stays in memory
165 until the application terminates.
166
167 \sa isLoaded(), unload()
168*/
169QPluginLoader::~QPluginLoader()
170{
171 if (d)
172 d->release();
173}
174
175/*!
176 Returns the root component object of the plugin. The plugin is
177 loaded if necessary. The function returns \nullptr if the plugin could
178 not be loaded or if the root component object could not be
179 instantiated.
180
181 If the root component object was destroyed, calling this function
182 creates a new instance.
183
184 The root component, returned by this function, is not deleted when
185 the QPluginLoader is destroyed. If you want to ensure that the root
186 component is deleted, you should call unload() as soon you don't
187 need to access the core component anymore. When the library is
188 finally unloaded, the root component will automatically be deleted.
189
190 The component object is a QObject. Use qobject_cast() to access
191 interfaces you are interested in.
192
193 \sa load()
194*/
195QObject *QPluginLoader::instance()
196{
197 if (!isLoaded() && !load())
198 return nullptr;
199 return d->pluginInstance();
200}
201
202/*!
203 Returns the meta data for this plugin. The meta data is data specified
204 in a json format using the Q_PLUGIN_METADATA() macro when compiling
205 the plugin.
206
207 The meta data can be queried in a fast and inexpensive way without
208 actually loading the plugin. This makes it possible to e.g. store
209 capabilities of the plugin in there, and make the decision whether to
210 load the plugin dependent on this meta data.
211 */
212QJsonObject QPluginLoader::metaData() const
213{
214 if (!d)
215 return QJsonObject();
216 return d->metaData;
217}
218
219/*!
220 Loads the plugin and returns \c true if the plugin was loaded
221 successfully; otherwise returns \c false. Since instance() always
222 calls this function before resolving any symbols it is not
223 necessary to call it explicitly. In some situations you might want
224 the plugin loaded in advance, in which case you would use this
225 function.
226
227 \sa unload()
228*/
229bool QPluginLoader::load()
230{
231 if (!d || d->fileName.isEmpty())
232 return false;
233 if (did_load)
234 return d->pHnd && d->instanceFactory.loadAcquire();
235 if (!d->isPlugin())
236 return false;
237 did_load = true;
238 return d->loadPlugin();
239}
240
241
242/*!
243 Unloads the plugin and returns \c true if the plugin could be
244 unloaded; otherwise returns \c false.
245
246 This happens automatically on application termination, so you
247 shouldn't normally need to call this function.
248
249 If other instances of QPluginLoader are using the same plugin, the
250 call will fail, and unloading will only happen when every instance
251 has called unload().
252
253 Don't try to delete the root component. Instead rely on
254 that unload() will automatically delete it when needed.
255
256 \sa instance(), load()
257*/
258bool QPluginLoader::unload()
259{
260 if (did_load) {
261 did_load = false;
262 return d->unload();
263 }
264 if (d) // Ouch
265 d->errorString = tr(s: "The plugin was not loaded.");
266 return false;
267}
268
269/*!
270 Returns \c true if the plugin is loaded; otherwise returns \c false.
271
272 \sa load()
273 */
274bool QPluginLoader::isLoaded() const
275{
276 return d && d->pHnd && d->instanceFactory.loadRelaxed();
277}
278
279#if defined(QT_SHARED)
280static QString locatePlugin(const QString& fileName)
281{
282 const bool isAbsolute = QDir::isAbsolutePath(path: fileName);
283 if (isAbsolute) {
284 QFileInfo fi(fileName);
285 if (fi.isFile()) {
286 return fi.canonicalFilePath();
287 }
288 }
289 QStringList prefixes = QLibraryPrivate::prefixes_sys();
290 prefixes.prepend(t: QString());
291 QStringList suffixes = QLibraryPrivate::suffixes_sys(fullVersion: QString());
292 suffixes.prepend(t: QString());
293
294 // Split up "subdir/filename"
295 const int slash = fileName.lastIndexOf(c: QLatin1Char('/'));
296 const QStringRef baseName = fileName.midRef(position: slash + 1);
297 const QStringRef basePath = isAbsolute ? QStringRef() : fileName.leftRef(n: slash + 1); // keep the '/'
298
299 const bool debug = qt_debug_component();
300
301 QStringList paths;
302 if (isAbsolute) {
303 paths.append(t: fileName.left(n: slash)); // don't include the '/'
304 } else {
305 paths = QCoreApplication::libraryPaths();
306 }
307
308 for (const QString &path : qAsConst(t&: paths)) {
309 for (const QString &prefix : qAsConst(t&: prefixes)) {
310 for (const QString &suffix : qAsConst(t&: suffixes)) {
311#ifdef Q_OS_ANDROID
312 {
313 QString pluginPath = basePath + prefix + baseName + suffix;
314 const QString fn = path + QLatin1String("/lib") + pluginPath.replace(QLatin1Char('/'), QLatin1Char('_'));
315 if (debug)
316 qDebug() << "Trying..." << fn;
317 if (QFileInfo(fn).isFile())
318 return fn;
319 }
320#endif
321 const QString fn = path + QLatin1Char('/') + basePath + prefix + baseName + suffix;
322 if (debug)
323 qDebug() << "Trying..." << fn;
324 if (QFileInfo(fn).isFile())
325 return fn;
326 }
327 }
328 }
329 if (debug)
330 qDebug() << fileName << "not found";
331 return QString();
332}
333#endif
334
335/*!
336 \property QPluginLoader::fileName
337 \brief the file name of the plugin
338
339 We recommend omitting the file's suffix in the file name, since
340 QPluginLoader will automatically look for the file with the appropriate
341 suffix (see QLibrary::isLibrary()).
342
343 When loading the plugin, QPluginLoader searches
344 in all plugin locations specified by QCoreApplication::libraryPaths(),
345 unless the file name has an absolute path. After loading the plugin
346 successfully, fileName() returns the fully-qualified file name of
347 the plugin, including the full path to the plugin if one was given
348 in the constructor or passed to setFileName().
349
350 If the file name does not exist, it will not be set. This property
351 will then contain an empty string.
352
353 By default, this property contains an empty string.
354
355 \sa load()
356*/
357void QPluginLoader::setFileName(const QString &fileName)
358{
359#if defined(QT_SHARED)
360 QLibrary::LoadHints lh = QLibrary::PreventUnloadHint;
361 if (d) {
362 lh = d->loadHints();
363 d->release();
364 d = nullptr;
365 did_load = false;
366 }
367
368 const QString fn = locatePlugin(fileName);
369
370 d = QLibraryPrivate::findOrCreate(fileName: fn, version: QString(), loadHints: lh);
371 if (!fn.isEmpty())
372 d->updatePluginState();
373
374#else
375 if (qt_debug_component()) {
376 qWarning("Cannot load %s into a statically linked Qt library.",
377 (const char*)QFile::encodeName(fileName));
378 }
379 Q_UNUSED(fileName);
380#endif
381}
382
383QString QPluginLoader::fileName() const
384{
385 if (d)
386 return d->fileName;
387 return QString();
388}
389
390/*!
391 \since 4.2
392
393 Returns a text string with the description of the last error that occurred.
394*/
395QString QPluginLoader::errorString() const
396{
397 return (!d || d->errorString.isEmpty()) ? tr(s: "Unknown error") : d->errorString;
398}
399
400/*! \since 4.4
401
402 \property QPluginLoader::loadHints
403 \brief Give the load() function some hints on how it should behave.
404
405 You can give hints on how the symbols in the plugin are
406 resolved. By default since Qt 5.7, QLibrary::PreventUnloadHint is set.
407
408 See the documentation of QLibrary::loadHints for a complete
409 description of how this property works.
410
411 \sa QLibrary::loadHints
412*/
413
414void QPluginLoader::setLoadHints(QLibrary::LoadHints loadHints)
415{
416 if (!d) {
417 d = QLibraryPrivate::findOrCreate(fileName: QString()); // ugly, but we need a d-ptr
418 d->errorString.clear();
419 }
420 d->setLoadHints(loadHints);
421}
422
423QLibrary::LoadHints QPluginLoader::loadHints() const
424{
425 return d ? d->loadHints() : QLibrary::LoadHints();
426}
427
428#endif // QT_CONFIG(library)
429
430typedef QVector<QStaticPlugin> StaticPluginList;
431Q_GLOBAL_STATIC(StaticPluginList, staticPluginList)
432
433/*!
434 \relates QPluginLoader
435 \since 5.0
436
437 Registers the \a plugin specified with the plugin loader, and is used
438 by Q_IMPORT_PLUGIN().
439*/
440void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin)
441{
442 staticPluginList()->append(t: plugin);
443}
444
445/*!
446 Returns a list of static plugin instances (root components) held
447 by the plugin loader.
448 \sa staticPlugins()
449*/
450QObjectList QPluginLoader::staticInstances()
451{
452 QObjectList instances;
453 const StaticPluginList *plugins = staticPluginList();
454 if (plugins) {
455 const int numPlugins = plugins->size();
456 instances.reserve(alloc: numPlugins);
457 for (int i = 0; i < numPlugins; ++i)
458 instances += plugins->at(i).instance();
459 }
460 return instances;
461}
462
463/*!
464 Returns a list of QStaticPlugins held by the plugin
465 loader. The function is similar to \l staticInstances()
466 with the addition that a QStaticPlugin also contains
467 meta data information.
468 \sa staticInstances()
469*/
470QVector<QStaticPlugin> QPluginLoader::staticPlugins()
471{
472 StaticPluginList *plugins = staticPluginList();
473 if (plugins)
474 return *plugins;
475 return QVector<QStaticPlugin>();
476}
477
478/*!
479 Returns a the meta data for the plugin as a QJsonObject.
480
481 \sa rawMetaData()
482*/
483QJsonObject QStaticPlugin::metaData() const
484{
485#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
486 // the data is already loaded, so this doesn't matter
487 qsizetype rawMetaDataSize = INT_MAX;
488 const char *ptr = rawMetaData();
489#else
490 auto ptr = static_cast<const char *>(rawMetaData);
491#endif
492
493 QString errMsg;
494 QJsonDocument doc = qJsonFromRawLibraryMetaData(raw: ptr, size: rawMetaDataSize, errMsg: &errMsg);
495 Q_ASSERT(doc.isObject());
496 Q_ASSERT(errMsg.isEmpty());
497 return doc.object();
498}
499
500QT_END_NAMESPACE
501
502#include "moc_qpluginloader.cpp"
503

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