Skip to content

Commit

Permalink
Merge pull request #5531 from boundlessgeo/fix-libpaths-update
Browse files Browse the repository at this point in the history
Fix updating QgsApplication::libraryPaths; load before any Qt plugin use

Note: could possibly be moved to QgsApplication::init()
  • Loading branch information
dakcarto authored Nov 9, 2017
2 parents 4c1ff1c + 29c71f4 commit 4c78526
Showing 1 changed file with 84 additions and 30 deletions.
114 changes: 84 additions & 30 deletions src/app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,90 @@ int main( int argc, char *argv[] )
}
}

// 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( QStringLiteral( "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( QStringLiteral( "QgsApplication::pluginPath removed from initial libraryPaths" ), 4 );
}
if ( libPaths.removeAll( QCoreApplication::applicationDirPath() ) )
{
QgsDebugMsgLevel( QStringLiteral( "QCoreApplication::applicationDirPath removed from initial libraryPaths" ), 4 );
}
// Prepend path, so a standard Qt bundle directory is parsed
QgsDebugMsgLevel( QStringLiteral( "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 + QStringLiteral( "/imageformats" ) )
&& QFile::exists( relLibPath + QStringLiteral( "/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( QStringLiteral( "QT_PLUGINS_DIR removed from initial libraryPaths" ), 4 );
}
// Prepend the Plugins path, so copied-in Qt plugin bundle directories are parsed.
QgsDebugMsgLevel( QStringLiteral( "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( QStringLiteral( "Prepending QT_PLUGINS_DIR to libraryPaths" ), 4 );
libPaths.prepend( QT_PLUGINS_DIR );
}
}

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

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

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

#ifdef Q_OS_MAC
// Set hidpi icons; use SVG icons, as PNGs will be relatively too small
QCoreApplication::setAttribute( Qt::AA_UseHighDpiPixmaps );
Expand Down Expand Up @@ -1085,36 +1169,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 4c78526

Please sign in to comment.