Exemple #1
0
// On Android, there there is a libqgis.so instead of a qgis executable.
// The main method symbol of this library needs to be exported so it can be called by java
// On Windows this main is included in qgis_app and called from mainwin.cpp
APP_EXPORT
#endif
int main( int argc, char *argv[] )
{
#ifdef Q_OS_MACX
  // Increase file resource limits (i.e., number of allowed open files)
  // (from code provided by Larry Biehl, Purdue University, USA, from 'MultiSpec' project)
  // This is generally 256 for the soft limit on Mac
  // NOTE: setrlimit() must come *before* initialization of stdio strings,
  //       e.g. before any debug messages, or setrlimit() gets ignored
  // see: http://stackoverflow.com/a/17726104/2865523
  struct rlimit rescLimit;
  if ( getrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 )
  {
    rlim_t oldSoft( rescLimit.rlim_cur );
    rlim_t oldHard( rescLimit.rlim_max );
#ifdef OPEN_MAX
    rlim_t newSoft( OPEN_MAX );
    rlim_t newHard( std::min( oldHard, newSoft ) );
#else
    rlim_t newSoft( 4096 );
    rlim_t newHard( std::min( ( rlim_t )8192, oldHard ) );
#endif
    if ( rescLimit.rlim_cur < newSoft )
    {
      rescLimit.rlim_cur = newSoft;
      rescLimit.rlim_max = newHard;

      if ( setrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 )
      {
        QgsDebugMsg( QString( "Mac RLIMIT_NOFILE Soft/Hard NEW: %1 / %2" )
                     .arg( rescLimit.rlim_cur ).arg( rescLimit.rlim_max ) );
      }
    }
    Q_UNUSED( oldSoft ); //avoid warnings
    QgsDebugMsg( QString( "Mac RLIMIT_NOFILE Soft/Hard ORIG: %1 / %2" )
                 .arg( oldSoft ).arg( oldHard ) );
  }
#endif

  QgsDebugMsg( QString( "Starting qgis main" ) );
#ifdef WIN32  // Windows
#ifdef _MSC_VER
  _set_fmode( _O_BINARY );
#else //MinGW
  _fmode = _O_BINARY;
#endif  // _MSC_VER
#endif  // WIN32

  // Set up the custom qWarning/qDebug custom handler
#ifndef ANDROID
  qInstallMsgHandler( myMessageOutput );
#endif

#if (defined(linux) && !defined(ANDROID)) || defined(__FreeBSD__)
  signal( SIGQUIT, qgisCrash );
  signal( SIGILL, qgisCrash );
  signal( SIGFPE, qgisCrash );
  signal( SIGSEGV, qgisCrash );
  signal( SIGBUS, qgisCrash );
  signal( SIGSYS, qgisCrash );
  signal( SIGTRAP, qgisCrash );
  signal( SIGXCPU, qgisCrash );
  signal( SIGXFSZ, qgisCrash );
#endif

#ifdef Q_OS_WIN
  SetUnhandledExceptionFilter( QgsCrashHandler::handle );
#endif

  // initialize random number seed
  qsrand( time( nullptr ) );

  /////////////////////////////////////////////////////////////////
  // Command line options 'behavior' flag setup
  ////////////////////////////////////////////////////////////////

  //
  // Parse the command line arguments, looking to see if the user has asked for any
  // special behaviors. Any remaining non command arguments will be kept aside to
  // be passed as a list of layers and / or a project that should be loaded.
  //

  // This behavior is used to load the app, snapshot the map,
  // save the image to disk and then exit
  QString mySnapshotFileName;
  QString configLocalStorageLocation;
  QString profileName;
  int mySnapshotWidth = 800;
  int mySnapshotHeight = 600;

  bool myHideSplash = false;
  bool mySettingsMigrationForce = false;
  bool mySkipVersionCheck = false;
#if defined(ANDROID)
  QgsDebugMsg( QString( "Android: Splash hidden" ) );
  myHideSplash = true;
#endif

  bool myRestoreDefaultWindowState = false;
  bool myRestorePlugins = true;
  bool myCustomization = true;

  QString dxfOutputFile;
  QgsDxfExport::SymbologyExport dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology;
  double dxfScale = 50000.0;
  QString dxfEncoding = QStringLiteral( "CP1252" );
  QString dxfPreset;
  QgsRectangle dxfExtent;

  // This behavior will set initial extent of map canvas, but only if
  // there are no command line arguments. This gives a usable map
  // extent when qgis starts with no layers loaded. When layers are
  // loaded, we let the layers define the initial extent.
  QString myInitialExtent;
  if ( argc == 1 )
    myInitialExtent = QStringLiteral( "-1,-1,1,1" );

  // This behavior will allow you to force the use of a translation file
  // which is useful for testing
  QString myTranslationCode;

  // The user can specify a path which will override the default path of custom
  // user settings (~/.qgis) and it will be used for QgsSettings INI file
  QString configpath;
  QString authdbdirectory;

  QString pythonfile;

  QString customizationfile;
  QString globalsettingsfile;

// TODO Fix android
#if defined(ANDROID)
  QgsDebugMsg( QString( "Android: All params stripped" ) );// Param %1" ).arg( argv[0] ) );
  //put all QGIS settings in the same place
  configpath = QgsApplication::qgisSettingsDirPath();
  QgsDebugMsg( QString( "Android: configpath set to %1" ).arg( configpath ) );
#endif

  QStringList args;

  if ( !bundleclicked( argc, argv ) )
  {
    // Build a local QCoreApplication from arguments. This way, arguments are correctly parsed from their native locale
    // It will use QString::fromLocal8Bit( argv ) under Unix and GetCommandLine() under Windows.
    QCoreApplication coreApp( argc, argv );
    args = QCoreApplication::arguments();

    for ( int i = 1; i < args.size(); ++i )
    {
      const QString &arg = args[i];

      if ( arg == QLatin1String( "--help" ) || arg == QLatin1String( "-?" ) )
      {
        usage( args[0] );
        return 2;
      }
      else if ( arg == QLatin1String( "--nologo" ) || arg == QLatin1String( "-n" ) )
      {
        myHideSplash = true;
      }
      else if ( arg == QLatin1String( "--version-migration" ) )
      {
        mySettingsMigrationForce = true;
      }
      else if ( arg == QLatin1String( "--noversioncheck" ) || arg == QLatin1String( "-V" ) )
      {
        mySkipVersionCheck = true;
      }
      else if ( arg == QLatin1String( "--noplugins" ) || arg == QLatin1String( "-P" ) )
      {
        myRestorePlugins = false;
      }
      else if ( arg == QLatin1String( "--nocustomization" ) || arg == QLatin1String( "-C" ) )
      {
        myCustomization = false;
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--profile" ) ) )
      {
        profileName = args[++i];
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--profiles-path" ) || arg == QLatin1String( "-s" ) ) )
      {
        configLocalStorageLocation = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--snapshot" ) || arg == QLatin1String( "-s" ) ) )
      {
        mySnapshotFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--width" ) || arg == QLatin1String( "-w" ) ) )
      {
        mySnapshotWidth = QString( args[++i] ).toInt();
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--height" ) || arg == QLatin1String( "-h" ) ) )
      {
        mySnapshotHeight = QString( args[++i] ).toInt();
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--lang" ) || arg == QLatin1String( "-l" ) ) )
      {
        myTranslationCode = args[++i];
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--project" ) || arg == QLatin1String( "-p" ) ) )
      {
        sProjectFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--extent" ) || arg == QLatin1String( "-e" ) ) )
      {
        myInitialExtent = args[++i];
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--authdbdirectory" ) || arg == QLatin1String( "-a" ) ) )
      {
        authdbdirectory = QDir::toNativeSeparators( QDir( args[++i] ).absolutePath() );
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--code" ) || arg == QLatin1String( "-f" ) ) )
      {
        pythonfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--customizationfile" ) || arg == QLatin1String( "-z" ) ) )
      {
        customizationfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
      }
      else if ( i + 1 < argc && ( arg == QLatin1String( "--globalsettingsfile" ) || arg == QLatin1String( "-g" ) ) )
      {
        globalsettingsfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
      }
      else if ( arg == QLatin1String( "--defaultui" ) || arg == QLatin1String( "-d" ) )
      {
        myRestoreDefaultWindowState = true;
      }
      else if ( arg == QLatin1String( "--dxf-export" ) )
      {
        dxfOutputFile = args[++i];
      }
      else if ( arg == QLatin1String( "--dxf-extent" ) )
      {
        QgsLocaleNumC l;
        QString ext( args[++i] );
        QStringList coords( ext.split( ',' ) );

        if ( coords.size() != 4 )
        {
          std::cerr << "invalid dxf extent " << ext.toStdString() << std::endl;
          return 2;
        }

        for ( int i = 0; i < 4; i++ )
        {
          bool ok;
          double d;

          d = coords[i].toDouble( &ok );
          if ( !ok )
          {
            std::cerr << "invalid dxf coordinate " << coords[i].toStdString() << " in extent " << ext.toStdString() << std::endl;
            return 2;
          }

          switch ( i )
          {
            case 0:
              dxfExtent.setXMinimum( d );
              break;
            case 1:
              dxfExtent.setYMinimum( d );
              break;
            case 2:
              dxfExtent.setXMaximum( d );
              break;
            case 3:
              dxfExtent.setYMaximum( d );
              break;
          }
        }
      }
      else if ( arg == QLatin1String( "--dxf-symbology-mode" ) )
      {
        QString mode( args[++i] );
        if ( mode == QLatin1String( "none" ) )
        {
          dxfSymbologyMode = QgsDxfExport::NoSymbology;
        }
        else if ( mode == QLatin1String( "symbollayer" ) )
        {
          dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology;
        }
        else if ( mode == QLatin1String( "feature" ) )
        {
          dxfSymbologyMode = QgsDxfExport::FeatureSymbology;
        }
        else
        {
          std::cerr << "invalid dxf symbology mode " << mode.toStdString() << std::endl;
          return 2;
        }
      }
      else if ( arg == QLatin1String( "--dxf-scale-denom" ) )
      {
        bool ok;
        QString scale( args[++i] );
        dxfScale = scale.toDouble( &ok );
        if ( !ok )
        {
          std::cerr << "invalid dxf scale " << scale.toStdString() << std::endl;
          return 2;
        }
      }
      else if ( arg == QLatin1String( "--dxf-encoding" ) )
      {
        dxfEncoding = args[++i];
      }
      else if ( arg == QLatin1String( "--dxf-preset" ) )
      {
        dxfPreset = args[++i];
      }
      else if ( arg == QLatin1String( "--" ) )
      {
        for ( i++; i < args.size(); ++i )
          sFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) );
      }
      else
      {
        sFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) );
      }
    }
  }

  /////////////////////////////////////////////////////////////////////
  // If no --project was specified, parse the args to look for a     //
  // .qgs file and set myProjectFileName to it. This allows loading  //
  // of a project file by clicking on it in various desktop managers //
  // where an appropriate mime-type has been set up.                 //
  /////////////////////////////////////////////////////////////////////
  if ( sProjectFileName.isEmpty() )
  {
    // check for a .qgs
    for ( int i = 0; i < args.size(); i++ )
    {
      QString arg = QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() );
      if ( arg.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) )
      {
        sProjectFileName = arg;
        break;
      }
    }
  }


  /////////////////////////////////////////////////////////////////////
  // Now we have the handlers for the different behaviors...
  ////////////////////////////////////////////////////////////////////

  /////////////////////////////////////////////////////////////////////
  // Initialize the application and the translation stuff
  /////////////////////////////////////////////////////////////////////

#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(ANDROID)
  bool myUseGuiFlag = nullptr != getenv( "DISPLAY" );
#else
  bool myUseGuiFlag = true;
#endif
  if ( !myUseGuiFlag )
  {
    std::cerr << QObject::tr(
                "QGIS starting in non-interactive mode not supported.\n"
                "You are seeing this message most likely because you "
                "have no DISPLAY environment variable set.\n"
              ).toUtf8().constData();
    exit( 1 ); //exit for now until a version of qgis is capabable of running non interactive
  }

  // GUI customization is enabled according to settings (loaded when instance is created)
  // we force disabled here if --nocustomization argument is used
  if ( !myCustomization )
  {
    QgsCustomization::instance()->setEnabled( false );
  }

  QCoreApplication::setOrganizationName( QgsApplication::QGIS_ORGANIZATION_NAME );
  QCoreApplication::setOrganizationDomain( QgsApplication::QGIS_ORGANIZATION_DOMAIN );
  QCoreApplication::setApplicationName( QgsApplication::QGIS_APPLICATION_NAME );
  QCoreApplication::setAttribute( Qt::AA_DontShowIconsInMenus, false );

  QgsSettings settings;
  if ( configLocalStorageLocation.isEmpty() )
  {
    if ( getenv( "QGIS_CUSTOM_CONFIG_PATH" ) )
    {
      configLocalStorageLocation = getenv( "QGIS_CUSTOM_CONFIG_PATH" );
    }
    else if ( settings.contains( QStringLiteral( "profilesPath" ), QgsSettings::Core ) )
    {
      configLocalStorageLocation = settings.value( QStringLiteral( "profilesPath" ), "", QgsSettings::Core ).toString();
      QgsDebugMsg( QString( "Loading profiles path from global config at %1" ).arg( configLocalStorageLocation ) );
    }

    // If it is still empty at this point we get it from the standard location.
    if ( configLocalStorageLocation.isEmpty() )
    {
      configLocalStorageLocation = QStandardPaths::standardLocations( QStandardPaths::AppDataLocation ).value( 0 );
    }
  }

  QString rootProfileFolder = QgsUserProfileManager::resolveProfilesFolder( configLocalStorageLocation );
  QgsUserProfileManager manager( rootProfileFolder );
  QgsUserProfile *profile = manager.getProfile( profileName, true );
  QString profileFolder = profile->folder();
  profileName = profile->name();
  delete profile;

  QgsDebugMsg( "User profile details:" );
  QgsDebugMsg( QString( "\t - %1" ).arg( profileName ) );
  QgsDebugMsg( QString( "\t - %1" ).arg( profileFolder ) );
  QgsDebugMsg( QString( "\t - %1" ).arg( rootProfileFolder ) );

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

  // SetUp the QgsSettings Global Settings:
  // - use the path specified with --globalsettingsfile path,
  // - use the environment if not found
  // - use a default location as a fallback
  if ( globalsettingsfile.isEmpty() )
  {
    globalsettingsfile = getenv( "QGIS_GLOBAL_SETTINGS_FILE" );
  }
  if ( globalsettingsfile.isEmpty() )
  {
    QString default_globalsettingsfile = QgsApplication::pkgDataPath() + "/qgis_global_settings.ini";
    if ( QFile::exists( default_globalsettingsfile ) )
    {
      globalsettingsfile = default_globalsettingsfile;
    }
  }
  if ( !globalsettingsfile.isEmpty() )
  {
    if ( ! QgsSettings::setGlobalSettingsPath( globalsettingsfile ) )
    {
      QgsMessageLog::logMessage( QStringLiteral( "Invalid globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" ) );
    }
    else
    {
      QgsMessageLog::logMessage( QStringLiteral( "Successfully loaded globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" ) );
    }
  }

  // Settings migration is only supported on the default profile for now.
  if ( profileName == "default" )
  {
    std::unique_ptr< QgsVersionMigration > migration( QgsVersionMigration::canMigrate( 20000, Qgis::QGIS_VERSION_INT ) );
    if ( migration && ( mySettingsMigrationForce || migration->requiresMigration() ) )
    {
      QgsDebugMsg( "RUNNING MIGRATION" );
      migration->runMigration();
    }
  }

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

  // Set 1024x1024 icon for dock, app switcher, etc., rendering
  myApp.setWindowIcon( QIcon( QgsApplication::iconsPath() + QStringLiteral( "qgis-icon-macos.png" ) ) );
#else
  myApp.setWindowIcon( QIcon( QgsApplication::appIconPath() ) );
#endif

  // TODO: use QgsSettings
  QSettings *customizationsettings = nullptr;

  // Using the customizationfile option always overrides the option and config path options.
  if ( !customizationfile.isEmpty() )
  {
    customizationsettings = new QSettings( customizationfile, QSettings::IniFormat );
    QgsCustomization::instance()->setEnabled( true );
  }
  else
  {
    customizationsettings = new QSettings( QStringLiteral( "QGIS" ), QStringLiteral( "QGISCUSTOMIZATION2" ) );
  }

  // Load and set possible default customization, must be done after QgsApplication init and QgsSettings ( QCoreApplication ) init
  QgsCustomization::instance()->setSettings( customizationsettings );
  QgsCustomization::instance()->loadDefault();

#ifdef Q_OS_MACX
  // If the GDAL plugins are bundled with the application and GDAL_DRIVER_PATH
  // is not already defined, use the GDAL plugins in the application bundle.
  QString gdalPlugins( QCoreApplication::applicationDirPath().append( "/lib/gdalplugins" ) );
  if ( QFile::exists( gdalPlugins ) && !getenv( "GDAL_DRIVER_PATH" ) )
  {
    setenv( "GDAL_DRIVER_PATH", gdalPlugins.toUtf8(), 1 );
  }

  // Point GDAL_DATA at any GDAL share directory embedded in the app bundle
  if ( !getenv( "GDAL_DATA" ) )
  {
    QStringList gdalShares;
    QString appResources( QDir::cleanPath( QgsApplication::pkgDataPath() ) );
    gdalShares << QCoreApplication::applicationDirPath().append( "/share/gdal" )
               << appResources.append( "/share/gdal" )
               << appResources.append( "/gdal" );
    Q_FOREACH ( const QString &gdalShare, gdalShares )
    {
      if ( QFile::exists( gdalShare ) )
      {
        setenv( "GDAL_DATA", gdalShare.toUtf8().constData(), 1 );
        break;
      }
    }
  }
Exemple #2
0
// GetOtherLibraries
//------------------------------------------------------------------------------
bool LinkerNode::GetOtherLibraries( NodeGraph & nodeGraph,
                                    const BFFIterator & iter,
                                    const Function * function,
                                    const AString & args,
                                    Dependencies & otherLibraries,
                                    bool msvc ) const
{
    // split to individual tokens
    Array< AString > tokens;
    args.Tokenize( tokens );

    bool ignoreAllDefaultLibs = false;
    Array< AString > defaultLibsToIgnore( 8, true );
    Array< AString > defaultLibs( 16, true );
    Array< AString > libs( 16, true );
    Array< AString > dashlLibs( 16, true );
    Array< AString > libPaths( 16, true );
    Array< AString > envLibPaths( 32, true );

    // extract lib path from system if present
    AStackString< 1024 > libVar;
    if ( Env::GetEnvVariable( "LIB", libVar ) )
    {
        libVar.Tokenize( envLibPaths, ';' );
    }

    const AString * const end = tokens.End();
    for ( const AString * it = tokens.Begin(); it != end; ++it )
    {
        const AString & token = *it;

        // MSVC style
        if ( msvc )
        {
            // /NODEFAULTLIB
            if ( LinkerNode::IsLinkerArg_MSVC( token, "NODEFAULTLIB" ) )
            {
                ignoreAllDefaultLibs = true;
                continue;
            }

            // /NODEFAULTLIB:
            if ( GetOtherLibsArg( "NODEFAULTLIB:", defaultLibsToIgnore, it, end, false, msvc ) )
            {
                continue;
            }

            // /DEFAULTLIB:
            if ( GetOtherLibsArg( "DEFAULTLIB:", defaultLibs, it, end, false, msvc ) )
            {
                continue;
            }

            // /LIBPATH:
            if ( GetOtherLibsArg( "LIBPATH:", libPaths, it, end, true, msvc ) ) // true = canonicalize path
            {
                continue;
            }

            // some other linker argument?
            if ( token.BeginsWith( '/' ) || token.BeginsWith( '-' ) )
            {
                continue;
            }
        }

        // GCC/SNC style
        if ( !msvc )
        {
            // We don't need to check for this, as there is no default lib passing on
            // the cmd line.
            // -nodefaultlibs
            //if ( token == "-nodefaultlibs" )
            //{
            //  ignoreAllDefaultLibs = true;
            //  continue;
            //}

            // -L (lib path)
            if ( GetOtherLibsArg( "L", libPaths, it, end, false, msvc ) )
            {
                continue;
            }

            // -l (lib)
            if ( GetOtherLibsArg( "l", dashlLibs, it, end, false, msvc ) )
            {
                continue;
            }

            // some other linker argument?
            if ( token.BeginsWith( '-' ) )
            {
                continue;
            }
        }

        // build time substitution?
        if ( token.BeginsWith( '%' ) ||     // %1
             token.BeginsWith( "'%" ) ||    // '%1'
             token.BeginsWith( "\"%" ) )    // "%1"
        {
            continue;
        }

        // anything left is an input to the linker
        AStackString<> libName;
        Args::StripQuotes( token.Get(), token.GetEnd(), libName );
        if ( token.IsEmpty() == false )
        {
            libs.Append( libName );
        }
    }

    // filter default libs
    if ( ignoreAllDefaultLibs )
    {
        // filter all default libs
        defaultLibs.Clear();
    }
    else
    {
        // filter specifically listed default libs
        const AString * const endI = defaultLibsToIgnore.End();
        for ( const AString * itI = defaultLibsToIgnore.Begin(); itI != endI; ++itI )
        {
            const AString * const endD = defaultLibs.End();
            for ( AString * itD = defaultLibs.Begin(); itD != endD; ++itD )
            {
                if ( itI->CompareI( *itD ) == 0 )
                {
                    defaultLibs.Erase( itD );
                    break;
                }
            }
        }
    }

    if ( !msvc )
    {
        // convert -l<name> style libs to lib<name>.a
        const AString * const endDL = dashlLibs.End();
        for ( const AString * itDL = dashlLibs.Begin(); itDL != endDL; ++itDL )
        {
            AStackString<> libName;
            libName += "lib";
            libName += *itDL;
            libName += ".a";
            libs.Append( libName );
        }
    }

    // any remaining default libs are treated the same as libs
    libs.Append( defaultLibs );

    // use Environment libpaths if found (but used after LIBPATH provided ones)
    libPaths.Append( envLibPaths );

    // convert libs to nodes
    const AString * const endL = libs.End();
    for ( const AString * itL = libs.Begin(); itL != endL; ++itL )
    {
        bool found = false;

        // is the file a full path?
        if ( ( itL->GetLength() > 2 ) && ( (*itL)[ 1 ] == ':' ) )
        {
            // check file exists in current location
            if ( !GetOtherLibrary( nodeGraph, iter, function, otherLibraries, AString::GetEmpty(), *itL, found ) )
            {
                return false; // GetOtherLibrary will have emitted error
            }
        }
        else
        {
            // check each libpath
            const AString * const endP = libPaths.End();
            for ( const AString * itP = libPaths.Begin(); itP != endP; ++itP )
            {
                if ( !GetOtherLibrary( nodeGraph, iter, function, otherLibraries, *itP, *itL, found ) )
                {
                    return false; // GetOtherLibrary will have emitted error
                }

                if ( found )
                {
                    break;
                }

                // keep searching lib paths...
            }
        }

        // file does not exist on disk, and there is no rule to build it
        // Don't complain about this, because:
        //  a) We may be parsing rules on another OS (i.e. parsing Linux rules on Windows)
        //  b) User may have filtered some libs for platforms they don't care about (i.e. libs
        //     for PS4 on a PC developer's machine on a cross-platform team)
        // If the file is actually needed, the linker will emit an error during link-time.
    }

    return true;
}