Skip to content

Commit

Permalink
Fix updating QgsApplication::libraryPaths; load before any Qt plugin use
Browse files Browse the repository at this point in the history
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 f80a705
Showing 1 changed file with 84 additions and 30 deletions.
114 changes: 84 additions & 30 deletions src/app/main.cpp
Expand Up @@ -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" );
#endif
#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 );
}
else
{
// 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 );
#endif

#ifdef Q_OS_MAC
// Set 1024x1024 icon for dock, app switcher, etc., rendering
myApp.setWindowIcon( QIcon( QgsApplication::iconsPath() + QLatin1String( "qgis-icon-macos.png" ) ) );
Expand Down Expand Up @@ -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" );
#endif
#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
myPath = QT_PLUGINS_DIR;
}

// 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 );
#endif

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

0 comments on commit f80a705

Please sign in to comment.