Skip to content

Commit 29c71f4

Browse files
committed
Fix updating QgsApplication::libraryPaths; load before any Qt plugin use
Previously, it was happening after setting the default window icon. For macOS, refactor libraryPaths update to ensure both QT_PLUGIN_PATH and qt.conf are properly honored (fixes 4.5 year old bug), with prioritization of qgis libs or libs shipped with .app bundle.
1 parent edcaf05 commit 29c71f4

File tree

1 file changed

+84
-30
lines changed

1 file changed

+84
-30
lines changed

src/app/main.cpp

+84-30
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,90 @@ int main( int argc, char *argv[] )
867867
}
868868
}
869869

870+
// Redefine QgsApplication::libraryPaths as necessary.
871+
// IMPORTANT: Do *after* QgsApplication myApp(...), but *before* Qt uses any plugins,
872+
// e.g. loading splash screen, setting window icon, etc.
873+
// Always honor QT_PLUGIN_PATH env var or qt.conf, which will
874+
// be part of libraryPaths just after QgsApplication creation.
875+
#ifdef Q_OS_WIN
876+
// For non static builds on win (static builds are not supported)
877+
// we need to be sure we can find the qt image plugins.
878+
QCoreApplication::addLibraryPath( QApplication::applicationDirPath()
879+
+ QDir::separator() + "qtplugins" );
880+
#endif
881+
#ifdef Q_OS_MAC
882+
// Resulting libraryPaths has critical QGIS plugin paths first, then any Qt plugin paths, then
883+
// any dev-defined paths (in app's qt.conf) and/or user-defined paths (QT_PLUGIN_PATH env var).
884+
//
885+
// NOTE: Minimizes, though does not fully protect against, crashes due to dev/user-defined libs
886+
// built against a different Qt/QGIS, while still allowing custom C++ plugins to load.
887+
QStringList libPaths( QCoreApplication::libraryPaths() );
888+
889+
QgsDebugMsgLevel( QStringLiteral( "Initial macOS QCoreApplication::libraryPaths: %1" )
890+
.arg( libPaths.join( " " ) ), 4 );
891+
892+
// Strip all critical paths that should always be prepended
893+
if ( libPaths.removeAll( QDir::cleanPath( QgsApplication::pluginPath() ) ) )
894+
{
895+
QgsDebugMsgLevel( QStringLiteral( "QgsApplication::pluginPath removed from initial libraryPaths" ), 4 );
896+
}
897+
if ( libPaths.removeAll( QCoreApplication::applicationDirPath() ) )
898+
{
899+
QgsDebugMsgLevel( QStringLiteral( "QCoreApplication::applicationDirPath removed from initial libraryPaths" ), 4 );
900+
}
901+
// Prepend path, so a standard Qt bundle directory is parsed
902+
QgsDebugMsgLevel( QStringLiteral( "Prepending QCoreApplication::applicationDirPath to libraryPaths" ), 4 );
903+
libPaths.prepend( QCoreApplication::applicationDirPath() );
904+
905+
// Check if we are running in a 'release' app bundle, i.e. contains copied-in
906+
// standard Qt-specific plugin subdirectories (ones never created by QGIS, e.g. 'sqldrivers' is).
907+
// Note: bundleclicked(...) is inadequate to determine which *type* of bundle was opened, e.g. release or build dir.
908+
// An app bundled with QGIS_MACAPP_BUNDLE > 0 is considered a release bundle.
909+
QString relLibPath( QDir::cleanPath( QCoreApplication::applicationDirPath().append( "/../PlugIns" ) ) );
910+
// Note: relLibPath becomes the defacto QT_PLUGINS_DIR of a release app bundle
911+
if ( QFile::exists( relLibPath + QStringLiteral( "/imageformats" ) )
912+
&& QFile::exists( relLibPath + QStringLiteral( "/codecs" ) ) )
913+
{
914+
// We are in a release app bundle.
915+
// Strip QT_PLUGINS_DIR because it will crash a launched release app bundle, since
916+
// the appropriate Qt frameworks and plugins have been copied into the bundle.
917+
if ( libPaths.removeAll( QT_PLUGINS_DIR ) )
918+
{
919+
QgsDebugMsgLevel( QStringLiteral( "QT_PLUGINS_DIR removed from initial libraryPaths" ), 4 );
920+
}
921+
// Prepend the Plugins path, so copied-in Qt plugin bundle directories are parsed.
922+
QgsDebugMsgLevel( QStringLiteral( "Prepending <bundle>/Plugins to libraryPaths" ), 4 );
923+
libPaths.prepend( relLibPath );
924+
925+
// TODO: see if this or another method can be used to avoid QCA's install prefix plugins
926+
// from being parsed and loaded (causes multi-Qt-loaded errors when bundled Qt should
927+
// be the only one loaded). QCA core (> v2.1.3) needs an update first.
928+
//setenv( "QCA_PLUGIN_PATH", relLibPath.toUtf8().constData(), 1 );
929+
}
930+
else
931+
{
932+
// We are either running from build dir bundle, or launching Mach-O binary directly.
933+
// Add system Qt plugins, since they are not bundled, and not always referenced by default.
934+
// An app bundled with QGIS_MACAPP_BUNDLE = 0 will still have Plugins/qgis in it.
935+
// Note: Don't always prepend.
936+
// User may have already defined it in QT_PLUGIN_PATH in a specific order.
937+
if ( !libPaths.contains( QT_PLUGINS_DIR ) )
938+
{
939+
QgsDebugMsgLevel( QStringLiteral( "Prepending QT_PLUGINS_DIR to libraryPaths" ), 4 );
940+
libPaths.prepend( QT_PLUGINS_DIR );
941+
}
942+
}
943+
944+
QgsDebugMsgLevel( QStringLiteral( "Prepending QgsApplication::pluginPath to libraryPaths" ), 4 );
945+
libPaths.prepend( QDir::cleanPath( QgsApplication::pluginPath() ) );
946+
947+
// Redefine library search paths.
948+
QCoreApplication::setLibraryPaths( libPaths );
949+
950+
QgsDebugMsgLevel( QStringLiteral( "Rewritten macOS QCoreApplication::libraryPaths: %1" )
951+
.arg( QCoreApplication::libraryPaths().join( " " ) ), 4 );
952+
#endif
953+
870954
#ifdef Q_OS_MAC
871955
// Set hidpi icons; use SVG icons, as PNGs will be relatively too small
872956
QCoreApplication::setAttribute( Qt::AA_UseHighDpiPixmaps );
@@ -1100,36 +1184,6 @@ int main( int argc, char *argv[] )
11001184
}
11011185
}
11021186

1103-
// For non static builds on mac and win (static builds are not supported)
1104-
// we need to be sure we can find the qt image
1105-
// plugins. In mac be sure to look in the
1106-
// application bundle...
1107-
#ifdef Q_OS_WIN
1108-
QCoreApplication::addLibraryPath( QApplication::applicationDirPath()
1109-
+ QDir::separator() + "qtplugins" );
1110-
#endif
1111-
#ifdef Q_OS_MACX
1112-
// IMPORTANT: do before Qt uses any plugins, e.g. before loading splash screen
1113-
QString myPath( QCoreApplication::applicationDirPath().append( "/../PlugIns" ) );
1114-
// Check if it contains a standard Qt-specific plugin subdirectory
1115-
if ( !QFile::exists( myPath + "/imageformats" ) )
1116-
{
1117-
// We are either running from build dir bundle, or launching binary directly.
1118-
// Use system Qt plugins, since they are not bundled.
1119-
// An app bundled with QGIS_MACAPP_BUNDLE=0 will still have Plugins/qgis in it
1120-
myPath = QT_PLUGINS_DIR;
1121-
}
1122-
1123-
// First clear the plugin search paths so we can be sure only plugins we define
1124-
// are being used. Note: this strips QgsApplication::pluginPath()
1125-
QStringList myPathList;
1126-
QCoreApplication::setLibraryPaths( myPathList );
1127-
1128-
QgsDebugMsg( QString( "Adding Mac QGIS and Qt plugins dirs to search path: %1" ).arg( myPath ) );
1129-
QCoreApplication::addLibraryPath( QgsApplication::pluginPath() );
1130-
QCoreApplication::addLibraryPath( myPath );
1131-
#endif
1132-
11331187
// set authentication database directory
11341188
if ( !authdbdirectory.isEmpty() )
11351189
{

0 commit comments

Comments
 (0)