main.cpp 15.9 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** 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.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
con's avatar
con committed
32
33
34

#include "qtsingleapplication.h"

35
#include <app/app_version.h>
con's avatar
con committed
36
#include <extensionsystem/iplugin.h>
37
#include <extensionsystem/pluginerroroverview.h>
Yuchen Deng's avatar
Yuchen Deng committed
38
39
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
con's avatar
con committed
40

41
42
43
44
45
46
47
48
49
50
51
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QLibraryInfo>
#include <QSettings>
#include <QTextStream>
#include <QThreadPool>
#include <QTimer>
#include <QTranslator>
#include <QUrl>
#include <QVariant>
con's avatar
con committed
52

53
#include <QNetworkProxyFactory>
54

55
56
57
58
#include <QApplication>
#include <QDesktopServices>
#include <QMainWindow>
#include <QMessageBox>
con's avatar
con committed
59

Marco Bubke's avatar
Marco Bubke committed
60
61
62
63
#ifdef ENABLE_QT_BREAKPAD
#include <qtsystemexceptionhandler.h>
#endif

64
enum { OptionIndent = 4, DescriptionIndent = 34 };
con's avatar
con committed
65

66
67
68
static const char appNameC[] = "Qt Creator";
static const char corePluginNameC[] = "Core";
static const char fixedOptionsC[] =
con's avatar
con committed
69
70
" [OPTION]... [FILE]...\n"
"Options:\n"
71
72
"    -help                         Display this help\n"
"    -version                      Display program version\n"
73
74
75
76
77
78
79
"    -client                       Attempt to connect to already running first instance\n"
"    -settingspath <path>          Override the default path where user settings are stored\n"
#if !defined(Q_OS_WIN)
"    -pid <pid>                    Attempt to connect to instance given by pid\n";
#else
;
#endif
con's avatar
con committed
80

81
82
83
84
85
86
87
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";
88
89
90
#if !defined(Q_OS_WIN)
static const char PID_OPTION[] = "-pid";
#endif
con's avatar
con committed
91

92
typedef QList<ExtensionSystem::PluginSpec *> PluginSpecSet;
con's avatar
con committed
93
94

// Helpers for displaying messages. Note that there is no console on Windows.
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
95
#ifdef Q_OS_WIN
con's avatar
con committed
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// 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)
{
121
    qWarning("%s", qPrintable(t));
con's avatar
con committed
122
123
124
125
}

static void displayError(const QString &t)
{
126
    qCritical("%s", qPrintable(t));
con's avatar
con committed
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
}

#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);
Yuchen Deng's avatar
Yuchen Deng committed
146
    str << "Usage: " << a0 << fixedOptionsC;
con's avatar
con committed
147
    ExtensionSystem::PluginManager::formatOptions(str, OptionIndent, DescriptionIndent);
Yuchen Deng's avatar
Yuchen Deng committed
148
    pm.formatPluginOptions(str, OptionIndent, DescriptionIndent);
con's avatar
con committed
149
150
151
152
153
154
155
156
    displayHelpText(help);
}

static inline QString msgCoreLoadFailure(const QString &why)
{
    return QCoreApplication::translate("Application", "Failed to load core: %1").arg(why);
}

157
static inline int askMsgSendFailed()
con's avatar
con committed
158
{
159
    return QMessageBox::question(0, QApplication::translate("Application","Could not send message"),
160
                                 QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. "
161
162
163
                                                             "It appears to be not responding. Do you want to start a new instance of Creator?"),
                                 QMessageBox::Yes | QMessageBox::No | QMessageBox::Retry,
                                 QMessageBox::Retry);
con's avatar
con committed
164
165
}

Eike Ziller's avatar
Eike Ziller committed
166
static inline QStringList getPluginPaths()
con's avatar
con committed
167
168
169
170
171
{
    QStringList rc;
    // Figure out root:  Up one from 'bin'
    QDir rootDir = QApplication::applicationDirPath();
    rootDir.cdUp();
172
    const QString rootDirPath = rootDir.canonicalPath();
173
#if !defined(Q_OS_MAC)
174
    // 1) "plugins" (Win/Linux)
con's avatar
con committed
175
    QString pluginPath = rootDirPath;
176
    pluginPath += QLatin1Char('/');
177
    pluginPath += QLatin1String(IDE_LIBRARY_BASENAME);
178
    pluginPath += QLatin1String("/qtcreator/plugins");
con's avatar
con committed
179
    rc.push_back(pluginPath);
180
#else
181
    // 2) "PlugIns" (OS X)
182
    QString pluginPath = rootDirPath;
183
    pluginPath += QLatin1String("/PlugIns");
con's avatar
con committed
184
    rc.push_back(pluginPath);
185
#endif
186
187
    // 3) <localappdata>/plugins/<ideversion>
    //    where <localappdata> is e.g.
188
    //    <drive>:\Users\<username>\AppData\Local\Nokia\qtcreator on Windows Vista and later
189
    //    $XDG_DATA_HOME or ~/.local/share/data/Nokia/qtcreator on Linux
190
    //    ~/Library/Application Support/Nokia/Qt Creator on Mac
191
    pluginPath = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
192
193
194
    pluginPath += QLatin1Char('/')
            + QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR)
            + QLatin1Char('/');
195
196
197
198
199
200
#if !defined(Q_OS_MAC)
    pluginPath += QLatin1String("qtcreator");
#else
    pluginPath += QLatin1String("Qt Creator");
#endif
    pluginPath += QLatin1String("/plugins/");
201
202
    pluginPath += QLatin1String(Core::Constants::IDE_VERSION_LONG);
    rc.push_back(pluginPath);
con's avatar
con committed
203
204
205
    return rc;
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
206
207
208
209
210
211
#ifdef Q_OS_MAC
#  define SHARE_PATH "/../Resources"
#else
#  define SHARE_PATH "/../share/qtcreator"
#endif

con's avatar
con committed
212
213
int main(int argc, char **argv)
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
214
#ifdef Q_OS_MAC
con's avatar
con committed
215
216
217
    // increase the number of file that can be opened in Qt Creator.
    struct rlimit rl;
    getrlimit(RLIMIT_NOFILE, &rl);
218
219

    rl.rlim_cur = qMin((rlim_t)OPEN_MAX, rl.rlim_max);
con's avatar
con committed
220
221
222
    setrlimit(RLIMIT_NOFILE, &rl);
#endif

223
224
225
226
227
#ifdef Q_WS_X11
    // QML is unusable with the xlib backend
    QApplication::setGraphicsSystem("raster");
#endif

con's avatar
con committed
228
    SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
229

230
231
232
    const int threadCount = QThreadPool::globalInstance()->maxThreadCount();
    QThreadPool::globalInstance()->setMaxThreadCount(qMax(4, 2 * threadCount));

Marco Bubke's avatar
Marco Bubke committed
233
234
235
236
#ifdef ENABLE_QT_BREAKPAD
    QtSystemExceptionHandler systemExceptionHandler;
#endif

237
238
239
240
    // 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;
Yuchen Deng's avatar
Yuchen Deng committed
241
    QStringList arguments = app.arguments(); // adapted arguments list is passed to plugin manager later
242
243
244
245
246
247
248
249
250
251
252
253
254
255
    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);

256
    // Must be done before any QSettings class is created
con's avatar
con committed
257
    QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope,
Yuchen Deng's avatar
Yuchen Deng committed
258
                       QCoreApplication::applicationDirPath() + QLatin1String(SHARE_PATH));
259
    QSettings::setDefaultFormat(QSettings::IniFormat);
260
261
    // plugin manager takes control of this settings object
    QSettings *settings = new QSettings(QSettings::IniFormat, QSettings::UserScope,
262
263
                                        QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
                                        QLatin1String("QtCreator"));
264
    QSettings *globalSettings = new QSettings(QSettings::IniFormat, QSettings::SystemScope,
265
266
                                              QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
                                              QLatin1String("QtCreator"));
267
268
    ExtensionSystem::PluginManager pluginManager;
    pluginManager.setFileExtension(QLatin1String("pluginspec"));
269
    pluginManager.setGlobalSettings(globalSettings);
270
271
    pluginManager.setSettings(settings);

272
273
274
275
276
277
278
279
280
281
282
    QTranslator translator;
    QTranslator qtTranslator;
    QStringList uiLanguages;
#if QT_VERSION >= 0x040800
    uiLanguages = QLocale::system().uiLanguages();
#else
    uiLanguages << QLocale::system().name();
#endif
    QString overrideLanguage = settings->value("General/OverrideLanguage").toString();
    if (!overrideLanguage.isEmpty())
        uiLanguages.prepend(overrideLanguage);
283
    const QString &creatorTrPath = QCoreApplication::applicationDirPath()
Yuchen Deng's avatar
Yuchen Deng committed
284
            + QLatin1String(SHARE_PATH "/translations");
285
286
287
288
289
290
291
292
293
294
295
    foreach (const QString &locale, uiLanguages) {
        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)) {
                app.installTranslator(&translator);
                app.installTranslator(&qtTranslator);
                app.setProperty("qtc_locale", locale);
                break;
            }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
296
            translator.load(QString()); // unload()
297
298
299
300
301
302
        } else if (locale == QLatin1String("C") /* overrideLanguage == "English" */) {
            // use built-in
            break;
        } else if (locale.startsWith(QLatin1String("en")) /* "English" is built-in */) {
            // use built-in
            break;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
303
304
305
        }
    }

306
    // Make sure we honor the system's proxy settings
307
#if defined(Q_OS_UNIX)
308
309
    QUrl proxyUrl(QString::fromLatin1(qgetenv("http_proxy")));
    if (proxyUrl.isValid()) {
310
        QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyUrl.host(),
311
312
313
                            proxyUrl.port(), proxyUrl.userName(), proxyUrl.password());
        QNetworkProxy::setApplicationProxy(proxy);
    }
Eike Ziller's avatar
Eike Ziller committed
314
# if defined(Q_OS_MAC) // unix and mac
315
316
317
    else {
        QNetworkProxyFactory::setUseSystemConfiguration(true);
    }
Eike Ziller's avatar
Eike Ziller committed
318
319
320
# endif
#else // windows
    QNetworkProxyFactory::setUseSystemConfiguration(true);
321
#endif
con's avatar
con committed
322
    // Load
Eike Ziller's avatar
Eike Ziller committed
323
    const QStringList pluginPaths = getPluginPaths();
con's avatar
con committed
324
325
    pluginManager.setPluginPaths(pluginPaths);

hjk's avatar
hjk committed
326
    QMap<QString, QString> foundAppOptions;
con's avatar
con committed
327
    if (arguments.size() > 1) {
hjk's avatar
hjk committed
328
        QMap<QString, bool> appOptions;
con's avatar
con committed
329
330
331
332
333
334
        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);
335
336
337
#if !defined(Q_OS_WIN)
        appOptions.insert(QLatin1String(PID_OPTION), true);
#endif
con's avatar
con committed
338
        QString errorMessage;
Yuchen Deng's avatar
Yuchen Deng committed
339
        if (!pluginManager.parseOptions(arguments, appOptions, &foundAppOptions, &errorMessage)) {
con's avatar
con committed
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
            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) {
355
        QString nativePaths = QDir::toNativeSeparators(pluginPaths.join(QLatin1String(",")));
356
        const QString reason = QCoreApplication::translate("Application", "Could not find 'Core.pluginspec' in %1").arg(nativePaths);
con's avatar
con committed
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
        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;
    }

376
377
378
379
380
381
382
383
384
385
386
387
388
    qint64 pid = -1;
#if !defined(Q_OS_WIN)
    if (foundAppOptions.contains(QLatin1String(PID_OPTION))) {
        QString pidString = foundAppOptions.value(QLatin1String(PID_OPTION));
        bool pidOk;
        qint64 tmpPid = pidString.toInt(&pidOk);
        if (pidOk)
            pid = tmpPid;
    }
#endif

    if (app.isRunning() && (pid != -1 || foundAppOptions.contains(QLatin1String(CLIENT_OPTION)))) {
        if (app.sendMessage(pluginManager.serializedArguments(), 5000 /*timeout*/, pid))
389
390
391
            return 0;

        // Message could not be send, maybe it was in the process of quitting
392
        if (app.isRunning(pid)) {
393
394
395
            // Nah app is still running, ask the user
            int button = askMsgSendFailed();
            while(button == QMessageBox::Retry) {
396
                if (app.sendMessage(pluginManager.serializedArguments(), 5000 /*timeout*/, pid))
397
                    return 0;
398
                if (!app.isRunning(pid)) // App quit while we were trying so start a new creator
399
400
401
402
403
404
                    button = QMessageBox::Yes;
                else
                    button = askMsgSendFailed();
            }
            if (button == QMessageBox::No)
                return -1;
405
406
        }
    }
con's avatar
con committed
407
408
409
410
411
412

    pluginManager.loadPlugins();
    if (coreplugin->hasError()) {
        displayError(msgCoreLoadFailure(coreplugin->errorString()));
        return 1;
    }
Yuchen Deng's avatar
Yuchen Deng committed
413
414
415
    if (pluginManager.hasError()) {
        ExtensionSystem::PluginErrorOverview errorOverview(&pluginManager);
        errorOverview.exec();
416
417
    }

418
419
420
421
    // Set up lock and remote arguments.
    app.initialize();
    QObject::connect(&app, SIGNAL(messageReceived(QString)),
                     &pluginManager, SLOT(remoteArguments(QString)));
Yuchen Deng's avatar
Yuchen Deng committed
422
423
424

    QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(),
                     SLOT(fileOpenRequest(QString)));
con's avatar
con committed
425

426
427
428
    // shutdown plugin manager on the exit
    QObject::connect(&app, SIGNAL(aboutToQuit()), &pluginManager, SLOT(shutdown()));

429
#ifdef WITH_TESTS
con's avatar
con committed
430
    // Do this after the event loop has started
Yuchen Deng's avatar
Yuchen Deng committed
431
    if (pluginManager.runningTests())
432
433
        QTimer::singleShot(100, &pluginManager, SLOT(startTests()));
#endif
434

435
    return app.exec();
con's avatar
con committed
436
}