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

KDE's Doxygen guidelines are available online.