forked from qt-creator/qt-creator
286 lines
9.7 KiB
C++
286 lines
9.7 KiB
C++
|
|
/***************************************************************************
|
||
|
|
**
|
||
|
|
** This file is part of Qt Creator
|
||
|
|
**
|
||
|
|
** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
|
||
|
|
**
|
||
|
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
||
|
|
**
|
||
|
|
**
|
||
|
|
** Non-Open Source Usage
|
||
|
|
**
|
||
|
|
** Licensees may use this file in accordance with the Qt Beta Version
|
||
|
|
** License Agreement, Agreement version 2.2 provided with the Software or,
|
||
|
|
** alternatively, in accordance with the terms contained in a written
|
||
|
|
** agreement between you and Nokia.
|
||
|
|
**
|
||
|
|
** GNU General Public License Usage
|
||
|
|
**
|
||
|
|
** Alternatively, this file may be used under the terms of the GNU General
|
||
|
|
** Public License versions 2.0 or 3.0 as published by the Free Software
|
||
|
|
** Foundation and appearing in the file LICENSE.GPL included in the packaging
|
||
|
|
** of this file. Please review the following information to ensure GNU
|
||
|
|
** General Public Licensing requirements will be met:
|
||
|
|
**
|
||
|
|
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
|
||
|
|
** http://www.gnu.org/copyleft/gpl.html.
|
||
|
|
**
|
||
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
||
|
|
** rights. These rights are described in the Nokia Qt GPL Exception version
|
||
|
|
** 1.2, included in the file GPL_EXCEPTION.txt in this package.
|
||
|
|
**
|
||
|
|
***************************************************************************/
|
||
|
|
|
||
|
|
#include "qtsingleapplication.h"
|
||
|
|
|
||
|
|
#include <extensionsystem/pluginmanager.h>
|
||
|
|
#include <extensionsystem/pluginspec.h>
|
||
|
|
#include <extensionsystem/iplugin.h>
|
||
|
|
|
||
|
|
#include <QtCore/QDir>
|
||
|
|
#include <QtCore/QTextStream>
|
||
|
|
#include <QtCore/QFileInfo>
|
||
|
|
#include <QtCore/QDebug>
|
||
|
|
#include <QtCore/QTimer>
|
||
|
|
|
||
|
|
#include <QtGui/QMessageBox>
|
||
|
|
#include <QtGui/QApplication>
|
||
|
|
#include <QtGui/QMainWindow>
|
||
|
|
|
||
|
|
#ifdef Q_OS_DARWIN
|
||
|
|
# include <sys/resource.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
enum { OptionIndent =4, DescriptionIndent = 24 };
|
||
|
|
|
||
|
|
static const char *appNameC = "Qt Creator";
|
||
|
|
static const char *corePluginNameC = "Core";
|
||
|
|
static const char *fixedOptionsC =
|
||
|
|
" [OPTION]... [FILE]...\n"
|
||
|
|
"Options:\n"
|
||
|
|
" -help Display this help\n"
|
||
|
|
" -version Display program version\n"
|
||
|
|
" -client Attempt to connect to already running instance\n";
|
||
|
|
|
||
|
|
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";
|
||
|
|
|
||
|
|
typedef QSet<ExtensionSystem::PluginSpec *> PluginSpecSet;
|
||
|
|
|
||
|
|
// Helpers for displaying messages. Note that there is no console on Windows.
|
||
|
|
#ifdef Q_WS_WIN
|
||
|
|
// Format as <pre> HTML
|
||
|
|
static inline void toHtml(QString &t)
|
||
|
|
{
|
||
|
|
t.replace(QLatin1Char('&'), QLatin1String("&"));
|
||
|
|
t.replace(QLatin1Char('<'), QLatin1String("<"));
|
||
|
|
t.replace(QLatin1Char('>'), QLatin1String(">"));
|
||
|
|
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)
|
||
|
|
{
|
||
|
|
qWarning(t.toUtf8().constData());
|
||
|
|
}
|
||
|
|
|
||
|
|
static void displayError(const QString &t)
|
||
|
|
{
|
||
|
|
qCritical(t.toUtf8().constData());
|
||
|
|
}
|
||
|
|
|
||
|
|
#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 QString msgSendArgumentFailed()
|
||
|
|
{
|
||
|
|
return QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. It appears to be not responding.");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Prepare a remote argument: If it is a relative file, add the current directory
|
||
|
|
// since the the central instance might be running in a different directory.
|
||
|
|
|
||
|
|
static inline QString prepareRemoteArgument(const QString &a)
|
||
|
|
{
|
||
|
|
QFileInfo fi(a);
|
||
|
|
if (!fi.exists())
|
||
|
|
return a;
|
||
|
|
if (fi.isRelative())
|
||
|
|
return fi.absoluteFilePath();
|
||
|
|
return a;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Send the arguments to an already running instance of Qt Creator
|
||
|
|
static bool sendArguments(SharedTools::QtSingleApplication &app, const QStringList &arguments)
|
||
|
|
{
|
||
|
|
if (!arguments.empty()) {
|
||
|
|
// Send off arguments
|
||
|
|
const QStringList::const_iterator acend = arguments.constEnd();
|
||
|
|
for (QStringList::const_iterator it = arguments.constBegin(); it != acend; ++it) {
|
||
|
|
if (!app.sendMessage(prepareRemoteArgument(*it))) {
|
||
|
|
displayError(msgSendArgumentFailed());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// Special empty argument means: Show and raise (the slot just needs to be triggered)
|
||
|
|
if (!app.sendMessage(QString())) {
|
||
|
|
displayError(msgSendArgumentFailed());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
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) "lib" dir
|
||
|
|
QString pluginPath = rootDirPath;
|
||
|
|
pluginPath += QDir::separator();
|
||
|
|
pluginPath += QLatin1String("lib");
|
||
|
|
rc.push_back(pluginPath);
|
||
|
|
// 2) "PlugIns"
|
||
|
|
pluginPath = rootDirPath;
|
||
|
|
pluginPath += QDir::separator();
|
||
|
|
pluginPath += QLatin1String("PlugIns");
|
||
|
|
rc.push_back(pluginPath);
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(int argc, char **argv)
|
||
|
|
{
|
||
|
|
#ifdef Q_OS_DARWIN
|
||
|
|
// increase the number of file that can be opened in Qt Creator.
|
||
|
|
struct rlimit rl;
|
||
|
|
getrlimit(RLIMIT_NOFILE, &rl);
|
||
|
|
rl.rlim_cur = rl.rlim_max;
|
||
|
|
setrlimit(RLIMIT_NOFILE, &rl);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv);
|
||
|
|
// Load
|
||
|
|
ExtensionSystem::PluginManager pluginManager;
|
||
|
|
pluginManager.setFileExtension(QLatin1String("pluginspec"));
|
||
|
|
|
||
|
|
const QStringList pluginPaths = getPluginPaths();
|
||
|
|
pluginManager.setPluginPaths(pluginPaths);
|
||
|
|
|
||
|
|
const QStringList arguments = app.arguments();
|
||
|
|
QMap<QString,QString> foundAppOptions;
|
||
|
|
if (arguments.size() > 1) {
|
||
|
|
QMap<QString,bool> appOptions;
|
||
|
|
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) {
|
||
|
|
const QString reason = QCoreApplication::translate("Application", "Couldn't find 'Core.pluginspec' in %1").arg(pluginPaths.join(QLatin1String(",")));
|
||
|
|
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)))
|
||
|
|
return sendArguments(app, pluginManager.arguments()) ? 0 : -1;
|
||
|
|
|
||
|
|
pluginManager.loadPlugins();
|
||
|
|
if (coreplugin->hasError()) {
|
||
|
|
displayError(msgCoreLoadFailure(coreplugin->errorString()));
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
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)), coreplugin->plugin(), SLOT(remoteArgument(QString)));
|
||
|
|
}
|
||
|
|
QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(), SLOT(remoteArgument(QString)));
|
||
|
|
|
||
|
|
// Do this after the event loop has started
|
||
|
|
QTimer::singleShot(100, &pluginManager, SLOT(startTests()));
|
||
|
|
return app.exec();
|
||
|
|
}
|