Files
qt-creator/src/app/main.cpp

385 lines
14 KiB
C++
Raw Normal View History

/**************************************************************************
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator
**
2011-01-11 16:28:15 +01:00
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
2008-12-02 12:01:29 +01:00
**
2011-04-13 08:42:33 +02:00
** Contact: Nokia Corporation (info@qt.nokia.com)
2008-12-02 12:01:29 +01:00
**
**
** GNU Lesser General Public License Usage
**
2011-04-13 08:42:33 +02:00
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
2010-12-17 16:01:08 +01:00
** In addition, as a special exception, Nokia gives you certain additional
2011-04-13 08:42:33 +02:00
** rights. These rights are described in the Nokia Qt LGPL Exception
2010-12-17 16:01:08 +01:00
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
2011-04-13 08:42:33 +02:00
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
2010-12-17 16:01:08 +01:00
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
2008-12-02 12:01:29 +01:00
**
**************************************************************************/
2008-12-02 12:01:29 +01:00
#include "qtsingleapplication.h"
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
#include <extensionsystem/iplugin.h>
#include <QtCore/QDir>
#include <QtCore/QUrl>
2008-12-02 12:01:29 +01:00
#include <QtCore/QTextStream>
#include <QtCore/QFileInfo>
#include <QtCore/QDebug>
#include <QtCore/QTimer>
2009-04-20 16:41:06 +02:00
#include <QtCore/QLibraryInfo>
#include <QtCore/QTranslator>
#include <QtCore/QSettings>
2009-04-20 16:41:06 +02:00
#include <QtCore/QVariant>
#include <QtCore/QThreadPool>
2008-12-02 12:01:29 +01:00
#include <QtNetwork/QNetworkProxyFactory>
2008-12-02 12:01:29 +01:00
#include <QtGui/QMessageBox>
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
#ifdef ENABLE_QT_BREAKPAD
#include <qtsystemexceptionhandler.h>
#endif
enum { OptionIndent = 4, DescriptionIndent = 34 };
2008-12-02 12:01:29 +01:00
static const char appNameC[] = "Qt Creator";
static const char corePluginNameC[] = "Core";
static const char fixedOptionsC[] =
2008-12-02 12:01:29 +01:00
" [OPTION]... [FILE]...\n"
"Options:\n"
" -help Display this help\n"
" -version Display program version\n"
" -client Attempt to connect to already running instance\n"
" -settingspath <path> Override the default path where user settings are stored.\n";
2008-12-02 12:01:29 +01:00
static const char HELP_OPTION1[] = "-h";
static const char HELP_OPTION2[] = "-help";
static const char HELP_OPTION3[] = "/h";
static const char HELP_OPTION4[] = "--help";
static const char VERSION_OPTION[] = "-version";
static const char CLIENT_OPTION[] = "-client";
static const char SETTINGS_OPTION[] = "-settingspath";
2008-12-02 12:01:29 +01:00
typedef QList<ExtensionSystem::PluginSpec *> PluginSpecSet;
2008-12-02 12:01:29 +01:00
// Helpers for displaying messages. Note that there is no console on Windows.
#ifdef Q_OS_WIN
2008-12-02 12:01:29 +01:00
// Format as <pre> HTML
static inline void toHtml(QString &t)
{
t.replace(QLatin1Char('&'), QLatin1String("&amp;"));
t.replace(QLatin1Char('<'), QLatin1String("&lt;"));
t.replace(QLatin1Char('>'), QLatin1String("&gt;"));
t.insert(0, QLatin1String("<html><pre>"));
t.append(QLatin1String("</pre></html>"));
}
static void displayHelpText(QString t) // No console on Windows.
{
toHtml(t);
QMessageBox::information(0, QLatin1String(appNameC), t);
}
static void displayError(const QString &t) // No console on Windows.
{
QMessageBox::critical(0, QLatin1String(appNameC), t);
}
#else
static void displayHelpText(const QString &t)
{
2008-12-19 10:49:29 +01:00
qWarning("%s", qPrintable(t));
2008-12-02 12:01:29 +01:00
}
static void displayError(const QString &t)
{
2008-12-19 10:49:29 +01:00
qCritical("%s", qPrintable(t));
2008-12-02 12:01:29 +01:00
}
#endif
static void printVersion(const ExtensionSystem::PluginSpec *coreplugin,
const ExtensionSystem::PluginManager &pm)
{
QString version;
QTextStream str(&version);
str << '\n' << appNameC << ' ' << coreplugin->version()<< " based on Qt " << qVersion() << "\n\n";
pm.formatPluginVersions(str);
str << '\n' << coreplugin->copyright() << '\n';
displayHelpText(version);
}
static void printHelp(const QString &a0, const ExtensionSystem::PluginManager &pm)
{
QString help;
QTextStream str(&help);
str << "Usage: " << a0 << fixedOptionsC;
ExtensionSystem::PluginManager::formatOptions(str, OptionIndent, DescriptionIndent);
pm.formatPluginOptions(str, OptionIndent, DescriptionIndent);
displayHelpText(help);
}
static inline QString msgCoreLoadFailure(const QString &why)
{
return QCoreApplication::translate("Application", "Failed to load core: %1").arg(why);
}
static inline int askMsgSendFailed()
2008-12-02 12:01:29 +01:00
{
return QMessageBox::question(0, QApplication::translate("Application","Could not send message"),
QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance."
"It appears to be not responding. Do you want to start a new instance of Creator?"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Retry,
QMessageBox::Retry);
2008-12-02 12:01:29 +01:00
}
static inline QStringList getPluginPaths()
{
QStringList rc;
// Figure out root: Up one from 'bin'
QDir rootDir = QApplication::applicationDirPath();
rootDir.cdUp();
const QString rootDirPath = rootDir.canonicalPath();
// 1) "plugins" (Win/Linux)
2008-12-02 12:01:29 +01:00
QString pluginPath = rootDirPath;
pluginPath += QLatin1Char('/');
pluginPath += QLatin1String(IDE_LIBRARY_BASENAME);
pluginPath += QLatin1Char('/');
pluginPath += QLatin1String("qtcreator");
pluginPath += QLatin1Char('/');
pluginPath += QLatin1String("plugins");
2008-12-02 12:01:29 +01:00
rc.push_back(pluginPath);
// 2) "PlugIns" (OS X)
2008-12-02 12:01:29 +01:00
pluginPath = rootDirPath;
pluginPath += QLatin1Char('/');
2008-12-02 12:01:29 +01:00
pluginPath += QLatin1String("PlugIns");
rc.push_back(pluginPath);
return rc;
}
2009-04-20 16:41:06 +02:00
#ifdef Q_OS_MAC
# define SHARE_PATH "/../Resources"
#else
# define SHARE_PATH "/../share/qtcreator"
#endif
2008-12-02 12:01:29 +01:00
int main(int argc, char **argv)
{
#ifdef Q_OS_MAC
2008-12-02 12:01:29 +01:00
// increase the number of file that can be opened in Qt Creator.
struct rlimit rl;
getrlimit(RLIMIT_NOFILE, &rl);
rl.rlim_cur = qMin((rlim_t)OPEN_MAX, rl.rlim_max);
2008-12-02 12:01:29 +01:00
setrlimit(RLIMIT_NOFILE, &rl);
#endif
SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv);
2009-04-20 16:41:06 +02:00
const int threadCount = QThreadPool::globalInstance()->maxThreadCount();
QThreadPool::globalInstance()->setMaxThreadCount(qMax(4, 2 * threadCount));
#ifdef ENABLE_QT_BREAKPAD
QtSystemExceptionHandler systemExceptionHandler;
#endif
2009-04-20 16:41:06 +02:00
QTranslator translator;
QTranslator qtTranslator;
QString locale = QLocale::system().name();
// Manually determine -settingspath command line option
// We can't use the regular way of the plugin manager, because that needs to parse pluginspecs
// but the settings path can influence which plugins are enabled
QString settingsPath;
QStringList arguments = app.arguments(); /* adapted arguments list is passed to plugin manager later */
QMutableStringListIterator it(arguments);
while (it.hasNext()) {
const QString &arg = it.next();
if (arg == QLatin1String(SETTINGS_OPTION)) {
it.remove();
if (it.hasNext()) {
settingsPath = QDir::fromNativeSeparators(it.next());
it.remove();
}
}
}
if (!settingsPath.isEmpty())
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settingsPath);
// Must be done before any QSettings class is created
2010-01-14 16:35:44 +01:00
QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope,
QCoreApplication::applicationDirPath()+QLatin1String(SHARE_PATH));
// plugin manager takes control of this settings object
QSettings *settings = new QSettings(QSettings::IniFormat, QSettings::UserScope,
QLatin1String("Nokia"), QLatin1String("QtCreator"));
ExtensionSystem::PluginManager pluginManager;
pluginManager.setFileExtension(QLatin1String("pluginspec"));
pluginManager.setSettings(settings);
locale = settings->value("General/OverrideLanguage", locale).toString();
const QString &creatorTrPath = QCoreApplication::applicationDirPath()
+ QLatin1String(SHARE_PATH "/translations");
if (translator.load(QLatin1String("qtcreator_") + locale, creatorTrPath)) {
const QString &qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
const QString &qtTrFile = QLatin1String("qt_") + locale;
// Binary installer puts Qt tr files into creatorTrPath
if (qtTranslator.load(qtTrFile, qtTrPath) || qtTranslator.load(qtTrFile, creatorTrPath)) {
2009-04-20 16:41:06 +02:00
app.installTranslator(&translator);
app.installTranslator(&qtTranslator);
app.setProperty("qtc_locale", locale);
} else {
translator.load(QString()); // unload()
}
}
// Make sure we honor the system's proxy settings
#if defined(Q_OS_UNIX)
QUrl proxyUrl(QString::fromLatin1(qgetenv("http_proxy")));
if (proxyUrl.isValid()) {
QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyUrl.host(),
proxyUrl.port(), proxyUrl.userName(), proxyUrl.password());
QNetworkProxy::setApplicationProxy(proxy);
}
#if defined(Q_OS_MAC)
else {
QNetworkProxyFactory::setUseSystemConfiguration(true);
}
#endif
#endif
2008-12-02 12:01:29 +01:00
// Load
const QStringList pluginPaths = getPluginPaths();
pluginManager.setPluginPaths(pluginPaths);
2008-12-05 18:29:19 +01:00
QMap<QString, QString> foundAppOptions;
2008-12-02 12:01:29 +01:00
if (arguments.size() > 1) {
2008-12-05 18:29:19 +01:00
QMap<QString, bool> appOptions;
2008-12-02 12:01:29 +01:00
appOptions.insert(QLatin1String(HELP_OPTION1), false);
appOptions.insert(QLatin1String(HELP_OPTION2), false);
appOptions.insert(QLatin1String(HELP_OPTION3), false);
appOptions.insert(QLatin1String(HELP_OPTION4), false);
appOptions.insert(QLatin1String(VERSION_OPTION), false);
appOptions.insert(QLatin1String(CLIENT_OPTION), false);
QString errorMessage;
if (!pluginManager.parseOptions(arguments,
appOptions,
&foundAppOptions,
&errorMessage)) {
displayError(errorMessage);
printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager);
return -1;
}
}
const PluginSpecSet plugins = pluginManager.plugins();
ExtensionSystem::PluginSpec *coreplugin = 0;
foreach (ExtensionSystem::PluginSpec *spec, plugins) {
if (spec->name() == QLatin1String(corePluginNameC)) {
coreplugin = spec;
break;
}
}
if (!coreplugin) {
QString nativePaths = QDir::toNativeSeparators(pluginPaths.join(QLatin1String(",")));
const QString reason = QCoreApplication::translate("Application", "Could not find 'Core.pluginspec' in %1").arg(nativePaths);
2008-12-02 12:01:29 +01:00
displayError(msgCoreLoadFailure(reason));
return 1;
}
if (coreplugin->hasError()) {
displayError(msgCoreLoadFailure(coreplugin->errorString()));
return 1;
}
if (foundAppOptions.contains(QLatin1String(VERSION_OPTION))) {
printVersion(coreplugin, pluginManager);
return 0;
}
if (foundAppOptions.contains(QLatin1String(HELP_OPTION1))
|| foundAppOptions.contains(QLatin1String(HELP_OPTION2))
|| foundAppOptions.contains(QLatin1String(HELP_OPTION3))
|| foundAppOptions.contains(QLatin1String(HELP_OPTION4))) {
printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager);
return 0;
}
const bool isFirstInstance = !app.isRunning();
if (!isFirstInstance && foundAppOptions.contains(QLatin1String(CLIENT_OPTION))) {
if (app.sendMessage(pluginManager.serializedArguments()))
return 0;
// Message could not be send, maybe it was in the process of quitting
if (app.isRunning()) {
// Nah app is still running, ask the user
int button = askMsgSendFailed();
while(button == QMessageBox::Retry) {
if (app.sendMessage(pluginManager.serializedArguments()))
return 0;
if (!app.isRunning()) // App quit while we were trying so start a new creator
button = QMessageBox::Yes;
else
button = askMsgSendFailed();
}
if (button == QMessageBox::No)
return -1;
}
}
2008-12-02 12:01:29 +01:00
pluginManager.loadPlugins();
if (coreplugin->hasError()) {
displayError(msgCoreLoadFailure(coreplugin->errorString()));
return 1;
}
{
QStringList errors;
foreach (ExtensionSystem::PluginSpec *p, pluginManager.plugins())
// only show errors on startup if plugin is enabled.
if (p->hasError() && p->isEnabled() && !p->isDisabledIndirectly())
errors.append(p->name() + "\n" + p->errorString());
if (!errors.isEmpty())
QMessageBox::warning(0,
QCoreApplication::translate("Application", "Qt Creator - Plugin loader messages"),
errors.join(QString::fromLatin1("\n\n")));
}
2008-12-02 12:01:29 +01:00
if (isFirstInstance) {
// Set up lock and remote arguments for the first instance only.
// Silently fallback to unconnected instances for any subsequent
// instances.
app.initialize();
QObject::connect(&app, SIGNAL(messageReceived(QString)),
&pluginManager, SLOT(remoteArguments(QString)));
2008-12-02 12:01:29 +01:00
}
QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(), SLOT(fileOpenRequest(QString)));
2008-12-02 12:01:29 +01:00
// shutdown plugin manager on the exit
QObject::connect(&app, SIGNAL(aboutToQuit()), &pluginManager, SLOT(shutdown()));
#ifdef WITH_TESTS
2008-12-02 12:01:29 +01:00
// Do this after the event loop has started
if(pluginManager.runningTests())
QTimer::singleShot(100, &pluginManager, SLOT(startTests()));
#endif
return app.exec();
2008-12-02 12:01:29 +01:00
}