QPluginLoader: fix loading of plugins with a relative file name
This makes QT_PLUGIN_PATH / QCoreApplication::libraryPaths() actually work, as a search path for plugins, when apps look for a specific plugin by name. To make it possible to write portable code (unlike the current QPluginLoader unittest), let QPluginLoader figure out the extension, too. Change-Id: I895d597d7cb05ded268734bc5f313f32d8d12cb9 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
parent
f6cc1f3aea
commit
418890e074
@ -92,6 +92,8 @@ public:
|
|||||||
QFunctionPointer resolve(const char *);
|
QFunctionPointer resolve(const char *);
|
||||||
|
|
||||||
static QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version = QString());
|
static QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version = QString());
|
||||||
|
static QStringList suffixes_sys(const QString &fullVersion);
|
||||||
|
static QStringList prefixes_sys();
|
||||||
|
|
||||||
static QVector<QStaticPlugin> staticPlugins();
|
static QVector<QStaticPlugin> staticPlugins();
|
||||||
|
|
||||||
|
@ -80,6 +80,60 @@ static QString qdlerror()
|
|||||||
return err ? QLatin1Char('(') + QString::fromLocal8Bit(err) + QLatin1Char(')'): QString();
|
return err ? QLatin1Char('(') + QString::fromLocal8Bit(err) + QLatin1Char(')'): QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion)
|
||||||
|
{
|
||||||
|
QStringList suffixes;
|
||||||
|
#if defined(Q_OS_HPUX)
|
||||||
|
// according to
|
||||||
|
// http://docs.hp.com/en/B2355-90968/linkerdifferencesiapa.htm
|
||||||
|
|
||||||
|
// In PA-RISC (PA-32 and PA-64) shared libraries are suffixed
|
||||||
|
// with .sl. In IPF (32-bit and 64-bit), the shared libraries
|
||||||
|
// are suffixed with .so. For compatibility, the IPF linker
|
||||||
|
// also supports the .sl suffix.
|
||||||
|
|
||||||
|
// But since we don't know if we are built on HPUX or HPUXi,
|
||||||
|
// we support both .sl (and .<version>) and .so suffixes but
|
||||||
|
// .so is preferred.
|
||||||
|
# if defined(__ia64)
|
||||||
|
if (!fullVersion.isEmpty()) {
|
||||||
|
suffixes << QString::fromLatin1(".so.%1").arg(fullVersion);
|
||||||
|
} else {
|
||||||
|
suffixes << QLatin1String(".so");
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
if (!fullVersion.isEmpty()) {
|
||||||
|
suffixes << QString::fromLatin1(".sl.%1").arg(fullVersion);
|
||||||
|
suffixes << QString::fromLatin1(".%1").arg(fullVersion);
|
||||||
|
} else {
|
||||||
|
suffixes << QLatin1String(".sl");
|
||||||
|
}
|
||||||
|
#elif defined(Q_OS_AIX)
|
||||||
|
suffixes << ".a";
|
||||||
|
|
||||||
|
#else
|
||||||
|
if (!fullVersion.isEmpty()) {
|
||||||
|
suffixes << QString::fromLatin1(".so.%1").arg(fullVersion);
|
||||||
|
} else {
|
||||||
|
suffixes << QLatin1String(".so");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
# ifdef Q_OS_MAC
|
||||||
|
if (!fullVersion.isEmpty()) {
|
||||||
|
suffixes << QString::fromLatin1(".%1.bundle").arg(fullVersion);
|
||||||
|
suffixes << QString::fromLatin1(".%1.dylib").arg(fullVersion);
|
||||||
|
} else {
|
||||||
|
suffixes << QLatin1String(".bundle") << QLatin1String(".dylib");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return suffixes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList QLibraryPrivate::prefixes_sys()
|
||||||
|
{
|
||||||
|
return QStringList() << QLatin1String("lib");
|
||||||
|
}
|
||||||
|
|
||||||
bool QLibraryPrivate::load_sys()
|
bool QLibraryPrivate::load_sys()
|
||||||
{
|
{
|
||||||
QString attempt;
|
QString attempt;
|
||||||
@ -96,50 +150,8 @@ bool QLibraryPrivate::load_sys()
|
|||||||
QStringList suffixes;
|
QStringList suffixes;
|
||||||
QStringList prefixes;
|
QStringList prefixes;
|
||||||
if (pluginState != IsAPlugin) {
|
if (pluginState != IsAPlugin) {
|
||||||
prefixes << QLatin1String("lib");
|
prefixes = prefixes_sys();
|
||||||
#if defined(Q_OS_HPUX)
|
suffixes = suffixes_sys(fullVersion);
|
||||||
// according to
|
|
||||||
// http://docs.hp.com/en/B2355-90968/linkerdifferencesiapa.htm
|
|
||||||
|
|
||||||
// In PA-RISC (PA-32 and PA-64) shared libraries are suffixed
|
|
||||||
// with .sl. In IPF (32-bit and 64-bit), the shared libraries
|
|
||||||
// are suffixed with .so. For compatibility, the IPF linker
|
|
||||||
// also supports the .sl suffix.
|
|
||||||
|
|
||||||
// But since we don't know if we are built on HPUX or HPUXi,
|
|
||||||
// we support both .sl (and .<version>) and .so suffixes but
|
|
||||||
// .so is preferred.
|
|
||||||
# if defined(__ia64)
|
|
||||||
if (!fullVersion.isEmpty()) {
|
|
||||||
suffixes << QString::fromLatin1(".so.%1").arg(fullVersion);
|
|
||||||
} else {
|
|
||||||
suffixes << QLatin1String(".so");
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
if (!fullVersion.isEmpty()) {
|
|
||||||
suffixes << QString::fromLatin1(".sl.%1").arg(fullVersion);
|
|
||||||
suffixes << QString::fromLatin1(".%1").arg(fullVersion);
|
|
||||||
} else {
|
|
||||||
suffixes << QLatin1String(".sl");
|
|
||||||
}
|
|
||||||
#elif defined(Q_OS_AIX)
|
|
||||||
suffixes << ".a";
|
|
||||||
|
|
||||||
#else
|
|
||||||
if (!fullVersion.isEmpty()) {
|
|
||||||
suffixes << QString::fromLatin1(".so.%1").arg(fullVersion);
|
|
||||||
} else {
|
|
||||||
suffixes << QLatin1String(".so");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
# ifdef Q_OS_MAC
|
|
||||||
if (!fullVersion.isEmpty()) {
|
|
||||||
suffixes << QString::fromLatin1(".%1.bundle").arg(fullVersion);
|
|
||||||
suffixes << QString::fromLatin1(".%1.dylib").arg(fullVersion);
|
|
||||||
} else {
|
|
||||||
suffixes << QLatin1String(".bundle") << QLatin1String(".dylib");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
int dlFlags = 0;
|
int dlFlags = 0;
|
||||||
#if defined(QT_HPUX_LD)
|
#if defined(QT_HPUX_LD)
|
||||||
|
@ -57,6 +57,17 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
extern QString qt_error_string(int code);
|
extern QString qt_error_string(int code);
|
||||||
|
|
||||||
|
QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion)
|
||||||
|
{
|
||||||
|
Q_UNUSED(fullVersion);
|
||||||
|
return QStringList() << ".dll";
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList QLibraryPrivate::prefixes_sys()
|
||||||
|
{
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
bool QLibraryPrivate::load_sys()
|
bool QLibraryPrivate::load_sys()
|
||||||
{
|
{
|
||||||
//avoid 'Bad Image' message box
|
//avoid 'Bad Image' message box
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include "qplatformdefs.h"
|
#include "qplatformdefs.h"
|
||||||
|
|
||||||
#include "qplugin.h"
|
#include "qplugin.h"
|
||||||
|
#include "qcoreapplication.h"
|
||||||
#include "qpluginloader.h"
|
#include "qpluginloader.h"
|
||||||
#include <qfileinfo.h>
|
#include <qfileinfo.h>
|
||||||
#include "qlibrary_p.h"
|
#include "qlibrary_p.h"
|
||||||
@ -250,14 +251,52 @@ bool QPluginLoader::isLoaded() const
|
|||||||
return d && d->pHnd && d->instance;
|
return d && d->pHnd && d->instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString locatePlugin(const QString& fileName)
|
||||||
|
{
|
||||||
|
QStringList prefixes = QLibraryPrivate::prefixes_sys();
|
||||||
|
prefixes.prepend(QString());
|
||||||
|
QStringList suffixes = QLibraryPrivate::suffixes_sys(QString());
|
||||||
|
suffixes.prepend(QString());
|
||||||
|
|
||||||
|
// Split up "subdir/filename"
|
||||||
|
const int slash = fileName.lastIndexOf('/');
|
||||||
|
const QString baseName = fileName.mid(slash + 1);
|
||||||
|
const QString basePath = fileName.left(slash + 1); // keep the '/'
|
||||||
|
|
||||||
|
const bool debug = qt_debug_component();
|
||||||
|
|
||||||
|
QStringList paths = QCoreApplication::libraryPaths();
|
||||||
|
paths.prepend(QStringLiteral("./")); // search in current dir first
|
||||||
|
foreach (const QString &path, paths) {
|
||||||
|
foreach (const QString &prefix, prefixes) {
|
||||||
|
foreach (const QString &suffix, suffixes) {
|
||||||
|
const QString fn = path + QLatin1Char('/') + basePath + prefix + baseName + suffix;
|
||||||
|
if (debug)
|
||||||
|
qDebug() << "Trying..." << fn;
|
||||||
|
if (QFileInfo(fn).isFile())
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (debug)
|
||||||
|
qDebug() << fileName << "not found";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\property QPluginLoader::fileName
|
\property QPluginLoader::fileName
|
||||||
\brief the file name of the plugin
|
\brief the file name of the plugin
|
||||||
|
|
||||||
To be loadable, the file's suffix must be a valid suffix for a
|
We recommend omitting the file's suffix in the file name, since
|
||||||
loadable library in accordance with the platform, e.g. \c .so on
|
QPluginLoader will automatically look for the file with the appropriate
|
||||||
Unix, \c .dylib on Mac OS X, and \c .dll on Windows. The suffix
|
suffix (see QLibrary::isLibrary()).
|
||||||
can be verified with QLibrary::isLibrary().
|
|
||||||
|
When loading the plugin, QPluginLoader searches in the current directory and
|
||||||
|
in all plugin locations specified by QCoreApplication::libraryPaths(),
|
||||||
|
unless the file name has an absolute path. After loading the plugin
|
||||||
|
successfully, fileName() returns the fully-qualified file name of
|
||||||
|
the plugin, including the full path to the plugin if one was given
|
||||||
|
in the constructor or passed to setFileName().
|
||||||
|
|
||||||
If the file name does not exist, it will not be set. This property
|
If the file name does not exist, it will not be set. This property
|
||||||
will then contain an empty string.
|
will then contain an empty string.
|
||||||
@ -277,7 +316,12 @@ void QPluginLoader::setFileName(const QString &fileName)
|
|||||||
did_load = false;
|
did_load = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString fn = QFileInfo(fileName).canonicalFilePath();
|
QFileInfo fi(fileName);
|
||||||
|
QString fn;
|
||||||
|
if (fi.isAbsolute())
|
||||||
|
fn = fi.canonicalFilePath();
|
||||||
|
else
|
||||||
|
fn = locatePlugin(fileName);
|
||||||
|
|
||||||
d = QLibraryPrivate::findOrCreate(fn);
|
d = QLibraryPrivate::findOrCreate(fn);
|
||||||
d->loadHints = lh;
|
d->loadHints = lh;
|
||||||
|
@ -4,6 +4,7 @@ HEADERS = almostplugin.h
|
|||||||
SOURCES = almostplugin.cpp
|
SOURCES = almostplugin.cpp
|
||||||
TARGET = almostplugin
|
TARGET = almostplugin
|
||||||
DESTDIR = ../bin
|
DESTDIR = ../bin
|
||||||
|
QT = core
|
||||||
*-g++*:QMAKE_LFLAGS -= -Wl,--no-undefined
|
*-g++*:QMAKE_LFLAGS -= -Wl,--no-undefined
|
||||||
|
|
||||||
# This is testdata for the tst_qpluginloader test.
|
# This is testdata for the tst_qpluginloader test.
|
||||||
|
@ -2,8 +2,11 @@ TEMPLATE = lib
|
|||||||
CONFIG += plugin
|
CONFIG += plugin
|
||||||
HEADERS = theplugin.h
|
HEADERS = theplugin.h
|
||||||
SOURCES = theplugin.cpp
|
SOURCES = theplugin.cpp
|
||||||
TARGET = $$qtLibraryTarget(theplugin)
|
# Use a predictable name for the plugin, no debug extension. Just like most apps do.
|
||||||
|
#TARGET = $$qtLibraryTarget(theplugin)
|
||||||
|
TARGET = theplugin
|
||||||
DESTDIR = ../bin
|
DESTDIR = ../bin
|
||||||
|
QT = core
|
||||||
|
|
||||||
# This is testdata for the tst_qpluginloader test.
|
# This is testdata for the tst_qpluginloader test.
|
||||||
target.path = $$[QT_INSTALL_TESTS]/tst_qpluginloader/bin
|
target.path = $$[QT_INSTALL_TESTS]/tst_qpluginloader/bin
|
||||||
|
@ -4,6 +4,7 @@ TARGET = ../tst_qpluginloader
|
|||||||
QT = core testlib
|
QT = core testlib
|
||||||
SOURCES = ../tst_qpluginloader.cpp
|
SOURCES = ../tst_qpluginloader.cpp
|
||||||
HEADERS = ../theplugin/plugininterface.h
|
HEADERS = ../theplugin/plugininterface.h
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
win32 {
|
win32 {
|
||||||
CONFIG(debug, debug|release) {
|
CONFIG(debug, debug|release) {
|
||||||
|
@ -59,7 +59,11 @@
|
|||||||
# define bundle_VALID true
|
# define bundle_VALID true
|
||||||
# define dylib_VALID true
|
# define dylib_VALID true
|
||||||
# define so_VALID true
|
# define so_VALID true
|
||||||
# define SUFFIX ".dylib"
|
//# ifdef QT_NO_DEBUG
|
||||||
|
# define SUFFIX ".dylib"
|
||||||
|
//# else
|
||||||
|
//# define SUFFIX "_debug.dylib"
|
||||||
|
//#endif
|
||||||
# define PREFIX "lib"
|
# define PREFIX "lib"
|
||||||
|
|
||||||
#elif defined(Q_OS_HPUX) && !defined(__ia64)
|
#elif defined(Q_OS_HPUX) && !defined(__ia64)
|
||||||
@ -79,11 +83,11 @@
|
|||||||
#elif defined(Q_OS_WIN)
|
#elif defined(Q_OS_WIN)
|
||||||
# undef dll_VALID
|
# undef dll_VALID
|
||||||
# define dll_VALID true
|
# define dll_VALID true
|
||||||
# ifdef QT_NO_DEBUG
|
//# ifdef QT_NO_DEBUG
|
||||||
# define SUFFIX ".dll"
|
# define SUFFIX ".dll"
|
||||||
# else
|
//# else
|
||||||
# define SUFFIX "d.dll"
|
//# define SUFFIX "d.dll"
|
||||||
# endif
|
//# endif
|
||||||
# define PREFIX ""
|
# define PREFIX ""
|
||||||
|
|
||||||
#else // all other Unix
|
#else // all other Unix
|
||||||
@ -111,6 +115,7 @@ private slots:
|
|||||||
#if defined (Q_OS_UNIX)
|
#if defined (Q_OS_UNIX)
|
||||||
void loadGarbage();
|
void loadGarbage();
|
||||||
#endif
|
#endif
|
||||||
|
void relativePath();
|
||||||
void reloadPlugin();
|
void reloadPlugin();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -294,6 +299,20 @@ void tst_QPluginLoader::loadGarbage()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void tst_QPluginLoader::relativePath()
|
||||||
|
{
|
||||||
|
// Windows binaries run from release and debug subdirs, so we can't rely on the current dir.
|
||||||
|
const QString binDir = QFINDTESTDATA("bin");
|
||||||
|
QVERIFY(!binDir.isEmpty());
|
||||||
|
QCoreApplication::addLibraryPath(binDir);
|
||||||
|
QPluginLoader loader("theplugin");
|
||||||
|
loader.load(); // not recommended, instance() should do the job.
|
||||||
|
PluginInterface *instance = qobject_cast<PluginInterface*>(loader.instance());
|
||||||
|
QVERIFY(instance);
|
||||||
|
QCOMPARE(instance->pluginName(), QLatin1String("Plugin ok"));
|
||||||
|
QVERIFY(loader.unload());
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QPluginLoader::reloadPlugin()
|
void tst_QPluginLoader::reloadPlugin()
|
||||||
{
|
{
|
||||||
QPluginLoader loader;
|
QPluginLoader loader;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user