From 418890e0748384eb684f33b10dc6f32493aee54b Mon Sep 17 00:00:00 2001 From: David Faure Date: Mon, 3 Dec 2012 12:29:10 +0100 Subject: 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 --- src/corelib/plugin/qlibrary_p.h | 2 + src/corelib/plugin/qlibrary_unix.cpp | 100 ++++++++++++--------- src/corelib/plugin/qlibrary_win.cpp | 11 +++ src/corelib/plugin/qpluginloader.cpp | 54 +++++++++-- .../qpluginloader/almostplugin/almostplugin.pro | 1 + .../plugin/qpluginloader/theplugin/theplugin.pro | 5 +- .../auto/corelib/plugin/qpluginloader/tst/tst.pro | 1 + .../plugin/qpluginloader/tst_qpluginloader.cpp | 29 ++++-- 8 files changed, 148 insertions(+), 55 deletions(-) diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h index 337c43acb9..8465d5cf06 100644 --- a/src/corelib/plugin/qlibrary_p.h +++ b/src/corelib/plugin/qlibrary_p.h @@ -92,6 +92,8 @@ public: QFunctionPointer resolve(const char *); static QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version = QString()); + static QStringList suffixes_sys(const QString &fullVersion); + static QStringList prefixes_sys(); static QVector staticPlugins(); diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index 2ef6f80466..35d8197306 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -80,6 +80,60 @@ static QString qdlerror() 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 .) 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() { QString attempt; @@ -96,50 +150,8 @@ bool QLibraryPrivate::load_sys() QStringList suffixes; QStringList prefixes; if (pluginState != IsAPlugin) { - prefixes << QLatin1String("lib"); -#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 .) 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 + prefixes = prefixes_sys(); + suffixes = suffixes_sys(fullVersion); } int dlFlags = 0; #if defined(QT_HPUX_LD) diff --git a/src/corelib/plugin/qlibrary_win.cpp b/src/corelib/plugin/qlibrary_win.cpp index 27796aedc1..ef3816a804 100644 --- a/src/corelib/plugin/qlibrary_win.cpp +++ b/src/corelib/plugin/qlibrary_win.cpp @@ -57,6 +57,17 @@ QT_BEGIN_NAMESPACE 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() { //avoid 'Bad Image' message box diff --git a/src/corelib/plugin/qpluginloader.cpp b/src/corelib/plugin/qpluginloader.cpp index 31d9fda858..c16a340dbe 100644 --- a/src/corelib/plugin/qpluginloader.cpp +++ b/src/corelib/plugin/qpluginloader.cpp @@ -42,6 +42,7 @@ #include "qplatformdefs.h" #include "qplugin.h" +#include "qcoreapplication.h" #include "qpluginloader.h" #include #include "qlibrary_p.h" @@ -250,14 +251,52 @@ bool QPluginLoader::isLoaded() const 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 \brief the file name of the plugin - To be loadable, the file's suffix must be a valid suffix for a - loadable library in accordance with the platform, e.g. \c .so on - Unix, \c .dylib on Mac OS X, and \c .dll on Windows. The suffix - can be verified with QLibrary::isLibrary(). + We recommend omitting the file's suffix in the file name, since + QPluginLoader will automatically look for the file with the appropriate + suffix (see 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 will then contain an empty string. @@ -277,7 +316,12 @@ void QPluginLoader::setFileName(const QString &fileName) 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->loadHints = lh; diff --git a/tests/auto/corelib/plugin/qpluginloader/almostplugin/almostplugin.pro b/tests/auto/corelib/plugin/qpluginloader/almostplugin/almostplugin.pro index 0004a8c712..70ab54a964 100644 --- a/tests/auto/corelib/plugin/qpluginloader/almostplugin/almostplugin.pro +++ b/tests/auto/corelib/plugin/qpluginloader/almostplugin/almostplugin.pro @@ -4,6 +4,7 @@ HEADERS = almostplugin.h SOURCES = almostplugin.cpp TARGET = almostplugin DESTDIR = ../bin +QT = core *-g++*:QMAKE_LFLAGS -= -Wl,--no-undefined # This is testdata for the tst_qpluginloader test. diff --git a/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.pro b/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.pro index b510f8fb44..2ea9c27cc9 100644 --- a/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.pro +++ b/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.pro @@ -2,8 +2,11 @@ TEMPLATE = lib CONFIG += plugin HEADERS = theplugin.h 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 +QT = core # This is testdata for the tst_qpluginloader test. target.path = $$[QT_INSTALL_TESTS]/tst_qpluginloader/bin diff --git a/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro b/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro index 48650a5727..a7a9661a54 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro +++ b/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro @@ -4,6 +4,7 @@ TARGET = ../tst_qpluginloader QT = core testlib SOURCES = ../tst_qpluginloader.cpp HEADERS = ../theplugin/plugininterface.h +CONFIG -= app_bundle win32 { CONFIG(debug, debug|release) { diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp index 155267f80b..34ec66f63f 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp @@ -59,7 +59,11 @@ # define bundle_VALID true # define dylib_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" #elif defined(Q_OS_HPUX) && !defined(__ia64) @@ -79,11 +83,11 @@ #elif defined(Q_OS_WIN) # undef dll_VALID # define dll_VALID true -# ifdef QT_NO_DEBUG +//# ifdef QT_NO_DEBUG # define SUFFIX ".dll" -# else -# define SUFFIX "d.dll" -# endif +//# else +//# define SUFFIX "d.dll" +//# endif # define PREFIX "" #else // all other Unix @@ -111,6 +115,7 @@ private slots: #if defined (Q_OS_UNIX) void loadGarbage(); #endif + void relativePath(); void reloadPlugin(); }; @@ -294,6 +299,20 @@ void tst_QPluginLoader::loadGarbage() } #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(loader.instance()); + QVERIFY(instance); + QCOMPARE(instance->pluginName(), QLatin1String("Plugin ok")); + QVERIFY(loader.unload()); +} + void tst_QPluginLoader::reloadPlugin() { QPluginLoader loader; -- cgit v1.2.3