Skip to content

Commit f80a705

Browse files
committedNov 9, 2017
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. Backported from master 4c78526
1 parent e744633 commit f80a705

File tree

1 file changed

+84
-30
lines changed

1 file changed

+84
-30
lines changed
 

‎src/app/main.cpp

Lines changed: 84 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,90 @@ int main( int argc, char *argv[] )
806806

807807
QgsApplication myApp( argc, argv, myUseGuiFlag, configpath );
808808

809+
// Redefine QgsApplication::libraryPaths as necessary.
810+
// IMPORTANT: Do *after* QgsApplication myApp(...), but *before* Qt uses any plugins,
811+
// e.g. loading splash screen, setting window icon, etc.
812+
// Always honor QT_PLUGIN_PATH env var or qt.conf, which will
813+
// be part of libraryPaths just after QgsApplication creation.
814+
#ifdef Q_OS_WIN
815+
// For non static builds on win (static builds are not supported)
816+
// we need to be sure we can find the qt image plugins.
817+
QCoreApplication::addLibraryPath( QApplication::applicationDirPath()
818+
+ QDir::separator() + "qtplugins" );
819+
#endif
820+
#ifdef Q_OS_MAC
821+
// Resulting libraryPaths has critical QGIS plugin paths first, then any Qt plugin paths, then
822+
// any dev-defined paths (in app's qt.conf) and/or user-defined paths (QT_PLUGIN_PATH env var).
823+
//
824+
// NOTE: Minimizes, though does not fully protect against, crashes due to dev/user-defined libs
825+
// built against a different Qt/QGIS, while still allowing custom C++ plugins to load.
826+
QStringList libPaths( QCoreApplication::libraryPaths() );
827+
828+
QgsDebugMsgLevel( QString( "Initial macOS QCoreApplication::libraryPaths: %1" )
829+
.arg( libPaths.join( " " ) ), 4 );
830+
831+
// Strip all critical paths that should always be prepended
832+
if ( libPaths.removeAll( QDir::cleanPath( QgsApplication::pluginPath() ) ) )
833+
{
834+
QgsDebugMsgLevel( QString( "QgsApplication::pluginPath removed from initial libraryPaths" ), 4 );
835+
}
836+
if ( libPaths.removeAll( QCoreApplication::applicationDirPath() ) )
837+
{
838+
QgsDebugMsgLevel( QString( "QCoreApplication::applicationDirPath removed from initial libraryPaths" ), 4 );
839+
}
840+
// Prepend path, so a standard Qt bundle directory is parsed
841+
QgsDebugMsgLevel( QString( "Prepending QCoreApplication::applicationDirPath to libraryPaths" ), 4 );
842+
libPaths.prepend( QCoreApplication::applicationDirPath() );
843+
844+
// Check if we are running in a 'release' app bundle, i.e. contains copied-in
845+
// standard Qt-specific plugin subdirectories (ones never created by QGIS, e.g. 'sqldrivers' is).
846+
// Note: bundleclicked(...) is inadequate to determine which *type* of bundle was opened, e.g. release or build dir.
847+
// An app bundled with QGIS_MACAPP_BUNDLE > 0 is considered a release bundle.
848+
QString relLibPath( QDir::cleanPath( QCoreApplication::applicationDirPath().append( "/../PlugIns" ) ) );
849+
// Note: relLibPath becomes the defacto QT_PLUGINS_DIR of a release app bundle
850+
if ( QFile::exists( relLibPath + "/imageformats" )
851+
&& QFile::exists( relLibPath + "/codecs" ) )
852+
{
853+
// We are in a release app bundle.
854+
// Strip QT_PLUGINS_DIR because it will crash a launched release app bundle, since
855+
// the appropriate Qt frameworks and plugins have been copied into the bundle.
856+
if ( libPaths.removeAll( QT_PLUGINS_DIR ) )
857+
{
858+
QgsDebugMsgLevel( QString( "QT_PLUGINS_DIR removed from initial libraryPaths" ), 4 );
859+
}
860+
// Prepend the Plugins path, so copied-in Qt plugin bundle directories are parsed.
861+
QgsDebugMsgLevel( QString( "Prepending <bundle>/Plugins to libraryPaths" ), 4 );
862+
libPaths.prepend( relLibPath );
863+
864+
// TODO: see if this or another method can be used to avoid QCA's install prefix plugins
865+
// from being parsed and loaded (causes multi-Qt-loaded errors when bundled Qt should
866+
// be the only one loaded). QCA core (> v2.1.3) needs an update first.
867+
//setenv( "QCA_PLUGIN_PATH", relLibPath.toUtf8().constData(), 1 );
868+
}
869+
else
870+
{
871+
// We are either running from build dir bundle, or launching Mach-O binary directly.
872+
// Add system Qt plugins, since they are not bundled, and not always referenced by default.
873+
// An app bundled with QGIS_MACAPP_BUNDLE = 0 will still have Plugins/qgis in it.
874+
// Note: Don't always prepend.
875+
// User may have already defined it in QT_PLUGIN_PATH in a specific order.
876+
if ( !libPaths.contains( QT_PLUGINS_DIR ) )
877+
{
878+
QgsDebugMsgLevel( QString( "Prepending QT_PLUGINS_DIR to libraryPaths" ), 4 );
879+
libPaths.prepend( QT_PLUGINS_DIR );
880+
}
881+
}
882+
883+
QgsDebugMsgLevel( QString( "Prepending QgsApplication::pluginPath to libraryPaths" ), 4 );
884+
libPaths.prepend( QDir::cleanPath( QgsApplication::pluginPath() ) );
885+
886+
// Redefine library search paths.
887+
QCoreApplication::setLibraryPaths( libPaths );
888+
889+
QgsDebugMsgLevel( QString( "Rewritten macOS QCoreApplication::libraryPaths: %1" )
890+
.arg( QCoreApplication::libraryPaths().join( " " ) ), 4 );
891+
#endif
892+
809893
#ifdef Q_OS_MAC
810894
// Set 1024x1024 icon for dock, app switcher, etc., rendering
811895
myApp.setWindowIcon( QIcon( QgsApplication::iconsPath() + QLatin1String( "qgis-icon-macos.png" ) ) );
@@ -1023,36 +1107,6 @@ int main( int argc, char *argv[] )
10231107
}
10241108
}
10251109

1026-
// For non static builds on mac and win (static builds are not supported)
1027-
// we need to be sure we can find the qt image
1028-
// plugins. In mac be sure to look in the
1029-
// application bundle...
1030-
#ifdef Q_OS_WIN
1031-
QCoreApplication::addLibraryPath( QApplication::applicationDirPath()
1032-
+ QDir::separator() + "qtplugins" );
1033-
#endif
1034-
#ifdef Q_OS_MACX
1035-
// IMPORTANT: do before Qt uses any plugins, e.g. before loading splash screen
1036-
QString myPath( QCoreApplication::applicationDirPath().append( "/../PlugIns" ) );
1037-
// Check if it contains a standard Qt-specific plugin subdirectory
1038-
if ( !QFile::exists( myPath + "/imageformats" ) )
1039-
{
1040-
// We are either running from build dir bundle, or launching binary directly.
1041-
// Use system Qt plugins, since they are not bundled.
1042-
// An app bundled with QGIS_MACAPP_BUNDLE=0 will still have Plugins/qgis in it
1043-
myPath = QT_PLUGINS_DIR;
1044-
}
1045-
1046-
// First clear the plugin search paths so we can be sure only plugins we define
1047-
// are being used. Note: this strips QgsApplication::pluginPath()
1048-
QStringList myPathList;
1049-
QCoreApplication::setLibraryPaths( myPathList );
1050-
1051-
QgsDebugMsg( QString( "Adding Mac QGIS and Qt plugins dirs to search path: %1" ).arg( myPath ) );
1052-
QCoreApplication::addLibraryPath( QgsApplication::pluginPath() );
1053-
QCoreApplication::addLibraryPath( myPath );
1054-
#endif
1055-
10561110
// set authentication database directory
10571111
if ( !authdbdirectory.isEmpty() )
10581112
{

0 commit comments

Comments
 (0)
Please sign in to comment.