Skip to content
Browse files

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
  • Loading branch information
dakcarto committed Nov 9, 2017
1 parent e744633 commit f80a705217d11e5dce8298c9ea97a0370e442ffc
Showing with 84 additions and 30 deletions.
  1. +84 −30 src/app/main.cpp
@@ -806,6 +806,90 @@ int main( int argc, char *argv[] )

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

// Redefine QgsApplication::libraryPaths as necessary.
// IMPORTANT: Do *after* QgsApplication myApp(...), but *before* Qt uses any plugins,
// e.g. loading splash screen, setting window icon, etc.
// Always honor QT_PLUGIN_PATH env var or qt.conf, which will
// be part of libraryPaths just after QgsApplication creation.
#ifdef Q_OS_WIN
// For non static builds on win (static builds are not supported)
// we need to be sure we can find the qt image plugins.
QCoreApplication::addLibraryPath( QApplication::applicationDirPath()
+ QDir::separator() + "qtplugins" );
#ifdef Q_OS_MAC
// Resulting libraryPaths has critical QGIS plugin paths first, then any Qt plugin paths, then
// any dev-defined paths (in app's qt.conf) and/or user-defined paths (QT_PLUGIN_PATH env var).
// NOTE: Minimizes, though does not fully protect against, crashes due to dev/user-defined libs
// built against a different Qt/QGIS, while still allowing custom C++ plugins to load.
QStringList libPaths( QCoreApplication::libraryPaths() );

QgsDebugMsgLevel( QString( "Initial macOS QCoreApplication::libraryPaths: %1" )
.arg( libPaths.join( " " ) ), 4 );

// Strip all critical paths that should always be prepended
if ( libPaths.removeAll( QDir::cleanPath( QgsApplication::pluginPath() ) ) )
QgsDebugMsgLevel( QString( "QgsApplication::pluginPath removed from initial libraryPaths" ), 4 );
if ( libPaths.removeAll( QCoreApplication::applicationDirPath() ) )
QgsDebugMsgLevel( QString( "QCoreApplication::applicationDirPath removed from initial libraryPaths" ), 4 );
// Prepend path, so a standard Qt bundle directory is parsed
QgsDebugMsgLevel( QString( "Prepending QCoreApplication::applicationDirPath to libraryPaths" ), 4 );
libPaths.prepend( QCoreApplication::applicationDirPath() );

// Check if we are running in a 'release' app bundle, i.e. contains copied-in
// standard Qt-specific plugin subdirectories (ones never created by QGIS, e.g. 'sqldrivers' is).
// Note: bundleclicked(...) is inadequate to determine which *type* of bundle was opened, e.g. release or build dir.
// An app bundled with QGIS_MACAPP_BUNDLE > 0 is considered a release bundle.
QString relLibPath( QDir::cleanPath( QCoreApplication::applicationDirPath().append( "/../PlugIns" ) ) );
// Note: relLibPath becomes the defacto QT_PLUGINS_DIR of a release app bundle
if ( QFile::exists( relLibPath + "/imageformats" )
&& QFile::exists( relLibPath + "/codecs" ) )
// We are in a release app bundle.
// Strip QT_PLUGINS_DIR because it will crash a launched release app bundle, since
// the appropriate Qt frameworks and plugins have been copied into the bundle.
if ( libPaths.removeAll( QT_PLUGINS_DIR ) )
QgsDebugMsgLevel( QString( "QT_PLUGINS_DIR removed from initial libraryPaths" ), 4 );
// Prepend the Plugins path, so copied-in Qt plugin bundle directories are parsed.
QgsDebugMsgLevel( QString( "Prepending <bundle>/Plugins to libraryPaths" ), 4 );
libPaths.prepend( relLibPath );

// TODO: see if this or another method can be used to avoid QCA's install prefix plugins
// from being parsed and loaded (causes multi-Qt-loaded errors when bundled Qt should
// be the only one loaded). QCA core (> v2.1.3) needs an update first.
//setenv( "QCA_PLUGIN_PATH", relLibPath.toUtf8().constData(), 1 );
// We are either running from build dir bundle, or launching Mach-O binary directly.
// Add system Qt plugins, since they are not bundled, and not always referenced by default.
// An app bundled with QGIS_MACAPP_BUNDLE = 0 will still have Plugins/qgis in it.
// Note: Don't always prepend.
// User may have already defined it in QT_PLUGIN_PATH in a specific order.
if ( !libPaths.contains( QT_PLUGINS_DIR ) )
QgsDebugMsgLevel( QString( "Prepending QT_PLUGINS_DIR to libraryPaths" ), 4 );
libPaths.prepend( QT_PLUGINS_DIR );

QgsDebugMsgLevel( QString( "Prepending QgsApplication::pluginPath to libraryPaths" ), 4 );
libPaths.prepend( QDir::cleanPath( QgsApplication::pluginPath() ) );

// Redefine library search paths.
QCoreApplication::setLibraryPaths( libPaths );

QgsDebugMsgLevel( QString( "Rewritten macOS QCoreApplication::libraryPaths: %1" )
.arg( QCoreApplication::libraryPaths().join( " " ) ), 4 );

#ifdef Q_OS_MAC
// Set 1024x1024 icon for dock, app switcher, etc., rendering
myApp.setWindowIcon( QIcon( QgsApplication::iconsPath() + QLatin1String( "qgis-icon-macos.png" ) ) );
@@ -1023,36 +1107,6 @@ int main( int argc, char *argv[] )

// For non static builds on mac and win (static builds are not supported)
// we need to be sure we can find the qt image
// plugins. In mac be sure to look in the
// application bundle...
#ifdef Q_OS_WIN
QCoreApplication::addLibraryPath( QApplication::applicationDirPath()
+ QDir::separator() + "qtplugins" );
#ifdef Q_OS_MACX
// IMPORTANT: do before Qt uses any plugins, e.g. before loading splash screen
QString myPath( QCoreApplication::applicationDirPath().append( "/../PlugIns" ) );
// Check if it contains a standard Qt-specific plugin subdirectory
if ( !QFile::exists( myPath + "/imageformats" ) )
// We are either running from build dir bundle, or launching binary directly.
// Use system Qt plugins, since they are not bundled.
// An app bundled with QGIS_MACAPP_BUNDLE=0 will still have Plugins/qgis in it

// First clear the plugin search paths so we can be sure only plugins we define
// are being used. Note: this strips QgsApplication::pluginPath()
QStringList myPathList;
QCoreApplication::setLibraryPaths( myPathList );

QgsDebugMsg( QString( "Adding Mac QGIS and Qt plugins dirs to search path: %1" ).arg( myPath ) );
QCoreApplication::addLibraryPath( QgsApplication::pluginPath() );
QCoreApplication::addLibraryPath( myPath );

// set authentication database directory
if ( !authdbdirectory.isEmpty() )

0 comments on commit f80a705

Please sign in to comment.
You can’t perform that action at this time.