KCoreAddons

kpluginloader.cpp
1 /*
2  * This file is part of the KDE project
3 
4  SPDX-FileCopyrightText: 2007 Bernhard Loos <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-only
7 */
8 
9 #include "kpluginloader.h"
10 
11 #include "kpluginfactory.h"
12 #include "kpluginmetadata.h"
13 
14 #include "kcoreaddons_debug.h"
15 #include <QCoreApplication>
16 #include <QDir>
17 #include <QDirIterator>
18 #include <QLibrary>
19 #include <QMutex>
20 
21 // TODO: Upstream the versioning stuff to Qt
22 // TODO: Patch for Qt to expose plugin-finding code directly
23 // TODO: Add a convenience method to KFactory to replace KPluginLoader::factory()
24 // TODO: (after the above) deprecate this class
25 
26 class KPluginLoaderPrivate
27 {
28  Q_DECLARE_PUBLIC(KPluginLoader)
29 public:
30  ~KPluginLoaderPrivate() = default;
31 
32 protected:
33  KPluginLoaderPrivate(const QString &libname)
34  : name(libname)
35  {
36  }
37 
38  KPluginLoader *q_ptr = nullptr;
39  const QString name;
40  QString errorString;
41  QPluginLoader *loader = nullptr;
42  quint32 pluginVersion = ~0U;
43  bool pluginVersionResolved = false;
44  bool isPluginMetaDataSet = false;
45 };
46 
48 {
49  // We just defer to Qt; unfortunately, QPluginLoader's searching code is not
50  // accessible without creating a QPluginLoader object.
51 
52  // Workaround for QTBUG-39642
53  static QMutex s_qtWorkaroundMutex;
54  QMutexLocker lock(&s_qtWorkaroundMutex);
55 
56  QPluginLoader loader(name);
57  return loader.fileName();
58 }
59 
61  : QObject(parent)
62  , d_ptr(new KPluginLoaderPrivate(plugin))
63 {
64  d_ptr->q_ptr = this;
65  Q_D(KPluginLoader);
66 
67  d->loader = new QPluginLoader(plugin, this);
68 }
69 
71  : QObject(parent)
72  , d_ptr(new KPluginLoaderPrivate(pluginName.name()))
73 {
74  d_ptr->q_ptr = this;
75  Q_D(KPluginLoader);
76 
77  d->loader = new QPluginLoader(this);
78 
79  if (pluginName.isValid()) {
80  d->loader->setFileName(pluginName.name());
81  if (d->loader->fileName().isEmpty()) {
82  qCDebug(KCOREADDONS_DEBUG) << "Failed to load plugin" << pluginName.name() << d->loader->errorString() << "\nPlugin search paths are"
83  << QCoreApplication::libraryPaths() << "\nThe environment variable QT_PLUGIN_PATH might be not correctly set";
84  }
85  } else {
86  d->errorString = pluginName.errorString();
87  }
88 }
89 
91 
93 {
94  Q_D(KPluginLoader);
95 
96  QObject *obj = instance();
97 
98  if (!obj) {
99  return nullptr;
100  }
101 
103 
104  if (factory == nullptr) {
105  qCDebug(KCOREADDONS_DEBUG) << "Expected a KPluginFactory, got a" << obj->metaObject()->className();
106  delete obj;
107  d->errorString = tr("The library %1 does not offer a KPluginFactory.").arg(d->name);
108  }
109 
110  if (!d->isPluginMetaDataSet && factory) {
111  factory->setMetaData(KPluginMetaData(*d->loader));
112  d->isPluginMetaDataSet = true;
113  }
114 
115  return factory;
116 }
117 
119 {
120  Q_D(const KPluginLoader);
121 
122  if (!load()) {
123  return qint32(-1);
124  }
125  return d->pluginVersion;
126 }
127 
129 {
130  Q_D(const KPluginLoader);
131 
132  return d->name;
133 }
134 
136 {
137  Q_D(const KPluginLoader);
138 
139  if (!d->errorString.isEmpty()) {
140  return d->errorString;
141  }
142 
143  return d->loader->errorString();
144 }
145 
147 {
148  Q_D(const KPluginLoader);
149  return d->loader->fileName();
150 }
151 
153 {
154  Q_D(const KPluginLoader);
155 
156  if (!load()) {
157  return nullptr;
158  }
159 
160  return d->loader->instance();
161 }
162 
164 {
165  Q_D(const KPluginLoader);
166 
167  return d->loader->isLoaded() && d->pluginVersionResolved;
168 }
169 
171 {
172  Q_D(KPluginLoader);
173 
174  if (!d->loader->load()) {
175  return false;
176  }
177 
178  if (d->pluginVersionResolved) {
179  return true;
180  }
181 
182  Q_ASSERT(!fileName().isEmpty());
183  QLibrary lib(fileName());
184  Q_ASSERT(lib.isLoaded()); // already loaded by QPluginLoader::load()
185 
186  // TODO: this messes up KPluginLoader::errorString(): it will change from unknown error to could not resolve kde_plugin_version
187  quint32 *version = reinterpret_cast<quint32 *>(lib.resolve("kde_plugin_version"));
188  if (version) {
189  d->pluginVersion = *version;
190  } else {
191  d->pluginVersion = ~0U;
192  }
193  d->pluginVersionResolved = true;
194 
195  return true;
196 }
197 
199 {
200  Q_D(const KPluginLoader);
201 
202  return d->loader->loadHints();
203 }
204 
206 {
207  Q_D(const KPluginLoader);
208 
209  return d->loader->metaData();
210 }
211 
213 {
214  Q_D(KPluginLoader);
215 
216  d->loader->setLoadHints(loadHints);
217 }
218 
220 {
221  Q_D(KPluginLoader);
222 
223  // Even if *this* call does not unload it, another might,
224  // so we err on the side of re-resolving the version.
225  d->pluginVersionResolved = false;
226 
227  return d->loader->unload();
228 }
229 
230 void KPluginLoader::forEachPlugin(const QString &directory, std::function<void(const QString &)> callback)
231 {
232  QStringList dirsToCheck;
233 #ifdef Q_OS_ANDROID
234  dirsToCheck << QCoreApplication::libraryPaths();
235 #else
236  if (QDir::isAbsolutePath(directory)) {
237  dirsToCheck << directory;
238  } else {
239  const QStringList listPaths = QCoreApplication::libraryPaths();
240  dirsToCheck.reserve(listPaths.size());
241  for (const QString &libDir : listPaths) {
242  dirsToCheck << libDir + QLatin1Char('/') + directory;
243  }
244  }
245 #endif
246 
247  qCDebug(KCOREADDONS_DEBUG) << "Checking for plugins in" << dirsToCheck;
248 
249  for (const QString &dir : qAsConst(dirsToCheck)) {
250  QDirIterator it(dir, QDir::Files);
251  while (it.hasNext()) {
252  it.next();
253 #ifdef Q_OS_ANDROID
254  QString prefix(QLatin1String("libplugins_") + QString(directory).replace(QLatin1Char('/'), QLatin1String("_")));
255  if (!prefix.endsWith(QLatin1Char('_'))) {
256  prefix.append(QLatin1Char('_'));
257  }
258  if (it.fileName().startsWith(prefix) && QLibrary::isLibrary(it.fileName())) {
259 #else
260  if (QLibrary::isLibrary(it.fileName())) {
261 #endif
262  callback(it.fileInfo().absoluteFilePath());
263  }
264  }
265  }
266 }
267 
268 QVector<KPluginMetaData> KPluginLoader::findPlugins(const QString &directory, std::function<bool(const KPluginMetaData &)> filter)
269 {
271  forEachPlugin(directory, [&](const QString &pluginPath) {
272  KPluginMetaData metadata(pluginPath);
273  if (!metadata.isValid()) {
274  return;
275  }
276  if (filter && !filter(metadata)) {
277  return;
278  }
279  ret.append(metadata);
280  });
281  return ret;
282 }
283 
285 {
286  auto filter = [&pluginId](const KPluginMetaData &md) -> bool {
287  return md.pluginId() == pluginId;
288  };
289  return KPluginLoader::findPlugins(directory, filter);
290 }
291 
292 QList<QObject *> KPluginLoader::instantiatePlugins(const QString &directory, std::function<bool(const KPluginMetaData &)> filter, QObject *parent)
293 {
294  QList<QObject *> ret;
295  QPluginLoader loader;
296  const QVector<KPluginMetaData> listMetaData = findPlugins(directory, filter);
297  for (const KPluginMetaData &metadata : listMetaData) {
298  loader.setFileName(metadata.fileName());
299  QObject *obj = loader.instance();
300  if (!obj) {
301  qCWarning(KCOREADDONS_DEBUG).nospace() << "Could not instantiate plugin \"" << metadata.fileName() << "\": " << loader.errorString();
302  continue;
303  }
304  obj->setParent(parent);
305  ret.append(obj);
306  }
307  return ret;
308 }
QString next()
quint32 pluginVersion()
Returns the plugin version.
QString & append(QChar ch)
QString pluginName() const
Returns the name of this plugin as given to the constructor.
QFileInfo fileInfo() const const
QString name(const QVariant &location)
void append(const T &value)
bool unload()
Attempts to unload the plugin.
void setMetaData(const KPluginMetaData &metaData)
Set the metadata about the plugin this factory generates.
QString name() const
The name of the plugin.
void reserve(int alloc)
QString fileName() const
Returns the path of the plugin.
static QVector< KPluginMetaData > findPluginsById(const QString &directory, const QString &pluginId)
Find all plugins inside directory with a given pluginId.
virtual const QMetaObject * metaObject() const const
QString fileName() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
Represents the name of a plugin intended for KPluginLoader.
int size() const const
bool load()
Loads the plugin.
void append(const T &value)
QObject * instance()
Returns the root object of the plugin.
QString errorString() const
Returns the last error.
This class behaves largely like QPluginLoader (and, indeed, uses it internally), but additionally rea...
Definition: kpluginloader.h:61
~KPluginLoader()
Destroys the plugin loader.
QString absoluteFilePath() const const
QJsonObject metaData() const
Returns the meta data for the plugin.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QObject * instance()
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString errorString() const const
static void forEachPlugin(const QString &directory, std::function< void(const QString &)> callback=std::function< void(const QString &)>())
Invokes callback for each valid plugin found inside directory.
typedef LoadHints
KPluginLoader(const QString &plugin, QObject *parent=nullptr)
Load a plugin by name.
static QString findPlugin(const QString &name)
Locates a plugin.
bool isLoaded() const const
void setLoadHints(QLibrary::LoadHints loadHints)
Set the load hints for the plugin.
QString errorString() const
The error string if no name could be determined.
void setParent(QObject *parent)
const char * className() const const
QStringList libraryPaths()
KPluginFactory * factory()
Returns the factory object of the plugin.
bool isAbsolutePath(const QString &path)
QFunctionPointer resolve(const char *symbol)
bool isValid() const
Whether the name is valid.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
This class allows easily accessing some standardized values from the JSON metadata that can be embedd...
static QList< QObject * > instantiatePlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter=std::function< bool(const KPluginMetaData &)>(), QObject *parent=nullptr)
Finds and instantiates (by calling QPluginLoader::instance()) all plugins from a given directory...
bool isLoaded() const
Determines whether the plugin is loaded.
static QVector< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter=std::function< bool(const KPluginMetaData &)>())
Find all plugins inside directory.
bool hasNext() const const
T qobject_cast(QObject *object)
QObject * parent() const const
KPluginFactory provides a convenient way to provide factory-style plugins.
QLibrary::LoadHints loadHints() const
Returns the load hints for the plugin.
bool isLibrary(const QString &fileName)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Apr 11 2021 23:01:47 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.