@@ -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 );
@@ -1085,36 +1169,6 @@ int main( int argc, char *argv[] )
10851169 }
10861170 }
10871171
1088- // For non static builds on mac and win (static builds are not supported)
1089- // we need to be sure we can find the qt image
1090- // plugins. In mac be sure to look in the
1091- // application bundle...
1092- #ifdef Q_OS_WIN
1093- QCoreApplication::addLibraryPath ( QApplication::applicationDirPath ()
1094- + QDir::separator () + " qtplugins" );
1095- #endif
1096- #ifdef Q_OS_MACX
1097- // IMPORTANT: do before Qt uses any plugins, e.g. before loading splash screen
1098- QString myPath ( QCoreApplication::applicationDirPath ().append ( " /../PlugIns" ) );
1099- // Check if it contains a standard Qt-specific plugin subdirectory
1100- if ( !QFile::exists ( myPath + " /imageformats" ) )
1101- {
1102- // We are either running from build dir bundle, or launching binary directly.
1103- // Use system Qt plugins, since they are not bundled.
1104- // An app bundled with QGIS_MACAPP_BUNDLE=0 will still have Plugins/qgis in it
1105- myPath = QT_PLUGINS_DIR;
1106- }
1107-
1108- // First clear the plugin search paths so we can be sure only plugins we define
1109- // are being used. Note: this strips QgsApplication::pluginPath()
1110- QStringList myPathList;
1111- QCoreApplication::setLibraryPaths ( myPathList );
1112-
1113- QgsDebugMsg ( QString ( " Adding Mac QGIS and Qt plugins dirs to search path: %1" ).arg ( myPath ) );
1114- QCoreApplication::addLibraryPath ( QgsApplication::pluginPath () );
1115- QCoreApplication::addLibraryPath ( myPath );
1116- #endif
1117-
11181172 // set authentication database directory
11191173 if ( !authdbdirectory.isEmpty () )
11201174 {
0 commit comments