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 <QLibrary>
15 #include <QDir>
16 #include <QDirIterator>
17 #include "kcoreaddons_debug.h"
18 #include <QCoreApplication>
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 protected:
30  KPluginLoaderPrivate(const QString &libname)
31  : name(libname),
32  loader(nullptr),
33  pluginVersion(~0U),
34  pluginVersionResolved(false)
35  {}
36  ~KPluginLoaderPrivate()
37  {}
38 
39  KPluginLoader *q_ptr;
40  const QString name;
41  QString errorString;
42  QPluginLoader *loader;
43  quint32 pluginVersion;
44  bool pluginVersionResolved;
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()
83  << "\nPlugin search paths are" << QCoreApplication::libraryPaths()
84  << "\nThe environment variable QT_PLUGIN_PATH might be not correctly set";
85  }
86  } else {
87  d->errorString = pluginName.errorString();
88  }
89 }
90 
92 {
93  delete d_ptr;
94 }
95 
97 {
98  Q_D(KPluginLoader);
99 
100  QObject *obj = instance();
101 
102  if (!obj) {
103  return nullptr;
104  }
105 
107 
108  if (factory == nullptr) {
109  qCDebug(KCOREADDONS_DEBUG) << "Expected a KPluginFactory, got a" << obj->metaObject()->className();
110  delete obj;
111  d->errorString = tr("The library %1 does not offer a KPluginFactory.").arg(d->name);
112  }
113 
114  return factory;
115 }
116 
118 {
119  Q_D(const KPluginLoader);
120 
121  if (!load()) {
122  return qint32(-1);
123  }
124  return d->pluginVersion;
125 }
126 
128 {
129  Q_D(const KPluginLoader);
130 
131  return d->name;
132 }
133 
135 {
136  Q_D(const KPluginLoader);
137 
138  if (!d->errorString.isEmpty()) {
139  return d->errorString;
140  }
141 
142  return d->loader->errorString();
143 }
144 
146 {
147  Q_D(const KPluginLoader);
148  return d->loader->fileName();
149 }
150 
152 {
153  Q_D(const KPluginLoader);
154 
155  if (!load()) {
156  return nullptr;
157  }
158 
159  return d->loader->instance();
160 }
161 
163 {
164  Q_D(const KPluginLoader);
165 
166  return d->loader->isLoaded() && d->pluginVersionResolved;
167 }
168 
170 {
171  Q_D(KPluginLoader);
172 
173  if (!d->loader->load()) {
174  return false;
175  }
176 
177  if (d->pluginVersionResolved) {
178  return true;
179  }
180 
181  Q_ASSERT(!fileName().isEmpty());
182  QLibrary lib(fileName());
183  Q_ASSERT(lib.isLoaded()); // already loaded by QPluginLoader::load()
184 
185  // TODO: this messes up KPluginLoader::errorString(): it will change from unknown error to could not resolve kde_plugin_version
186  quint32 *version = reinterpret_cast<quint32 *>(lib.resolve("kde_plugin_version"));
187  if (version) {
188  d->pluginVersion = *version;
189  } else {
190  d->pluginVersion = ~0U;
191  }
192  d->pluginVersionResolved = true;
193 
194  return true;
195 }
196 
198 {
199  Q_D(const KPluginLoader);
200 
201  return d->loader->loadHints();
202 }
203 
205 {
206  Q_D(const KPluginLoader);
207 
208  return d->loader->metaData();
209 }
210 
212 {
213  Q_D(KPluginLoader);
214 
215  d->loader->setLoadHints(loadHints);
216 }
217 
219 {
220  Q_D(KPluginLoader);
221 
222  // Even if *this* call does not unload it, another might,
223  // so we err on the side of re-resolving the version.
224  d->pluginVersionResolved = false;
225 
226  return d->loader->unload();
227 }
228 
229 
230 void KPluginLoader::forEachPlugin(const QString &directory, std::function<void(const QString &)> callback)
231 {
232  QStringList dirsToCheck;
233  if (QDir::isAbsolutePath(directory)) {
234  dirsToCheck << directory;
235  } else {
236  const QStringList listPaths = QCoreApplication::libraryPaths();
237  for (const QString &libDir : listPaths) {
238  dirsToCheck << libDir + QLatin1Char('/') + directory;
239  }
240  }
241 
242  qCDebug(KCOREADDONS_DEBUG) << "Checking for plugins in" << dirsToCheck;
243 
244  for (const QString &dir : qAsConst(dirsToCheck)) {
245  QDirIterator it(dir, QDir::Files);
246  while (it.hasNext()) {
247  it.next();
248  if (QLibrary::isLibrary(it.fileName())) {
249  callback(it.fileInfo().absoluteFilePath());
250  }
251  }
252  }
253 }
254 
255 QVector<KPluginMetaData> KPluginLoader::findPlugins(const QString &directory, std::function<bool(const KPluginMetaData &)> filter)
256 {
258  forEachPlugin(directory, [&](const QString &pluginPath) {
259  KPluginMetaData metadata(pluginPath);
260  if (!metadata.isValid()) {
261  return;
262  }
263  if (filter && !filter(metadata)) {
264  return;
265  }
266  ret.append(metadata);
267  });
268  return ret;
269 }
270 
272 {
273  auto filter = [&pluginId](const KPluginMetaData &md) -> bool
274  {
275  return md.pluginId() == pluginId;
276  };
277  return KPluginLoader::findPlugins(directory, filter);
278 }
279 
281  std::function<bool(const KPluginMetaData &)> filter, QObject* parent)
282 {
283  QList<QObject *> ret;
284  QPluginLoader loader;
285  const QVector<KPluginMetaData> listMetaData = findPlugins(directory, filter);
286  for (const KPluginMetaData &metadata : listMetaData) {
287  loader.setFileName(metadata.fileName());
288  QObject* obj = loader.instance();
289  if (!obj) {
290  qCWarning(KCOREADDONS_DEBUG).nospace() << "Could not instantiate plugin \"" << metadata.fileName() << "\": "
291  << loader.errorString();
292  continue;
293  }
294  obj->setParent(parent);
295  ret.append(obj);
296  }
297  return ret;
298 }
QString next()
quint32 pluginVersion()
Returns the plugin version.
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.
QString name() const
The name of the plugin.
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.
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:60
~KPluginLoader()
Destroys the plugin loader.
QString absoluteFilePath() const const
QJsonObject metaData() const
Returns the meta data for the plugin.
QObject * instance()
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.
bool isValid() const
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-2020 The KDE developers.
Generated on Wed Jul 1 2020 23:00:14 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.