Files
qt-creator/src/app/main.cpp
Eike Ziller d6c95ef365 Take compatibility version into account for user plugins
When loading user-local plugins, do not load plugins for all patch
versions starting from .0 unconditionally. Take the compatibility
version into account.

That fixes that prereleases x.y.82 etc should not try loading plugins
for any actual x.y.z release.

Change-Id: Ide0931bbdef4f48e08dcc3213f7c193c8889fb0f
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2020-03-06 09:49:05 +00:00

704 lines
28 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "../tools/qtcreatorcrashhandler/crashhandlersetup.h"
#include <app/app_version.h>
#include <extensionsystem/iplugin.h>
#include <extensionsystem/pluginerroroverview.h>
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
#include <qtsingleapplication.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/optional.h>
#include <utils/temporarydirectory.h>
#include <QDebug>
#include <QDir>
#include <QFontDatabase>
#include <QFileInfo>
#include <QLibraryInfo>
#include <QSettings>
#include <QStyle>
#include <QTextStream>
#include <QThreadPool>
#include <QTimer>
#include <QTranslator>
#include <QUrl>
#include <QVariant>
#include <QNetworkProxyFactory>
#include <QApplication>
#include <QMessageBox>
#include <QProcess>
#include <QStandardPaths>
#include <QTemporaryDir>
#include <string>
#include <vector>
#include <iterator>
#ifdef ENABLE_QT_BREAKPAD
#include <qtsystemexceptionhandler.h>
#endif
using namespace ExtensionSystem;
enum { OptionIndent = 4, DescriptionIndent = 34 };
const char corePluginNameC[] = "Core";
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 first instance\n"
" -settingspath <path> Override the default path where user settings are stored\n"
" -installsettingspath <path> Override the default path from where user-independent settings are read\n"
" -temporarycleansettings Use clean settings for debug or testing reasons\n"
" -pid <pid> Attempt to connect to instance given by pid\n"
" -block Block until editor is closed\n"
" -pluginpath <path> Add a custom search path for plugins\n";
const char HELP_OPTION1[] = "-h";
const char HELP_OPTION2[] = "-help";
const char HELP_OPTION3[] = "/h";
const char HELP_OPTION4[] = "--help";
const char VERSION_OPTION[] = "-version";
const char CLIENT_OPTION[] = "-client";
const char SETTINGS_OPTION[] = "-settingspath";
const char INSTALL_SETTINGS_OPTION[] = "-installsettingspath";
const char TEST_OPTION[] = "-test";
const char TEMPORARY_CLEAN_SETTINGS1[] = "-temporarycleansettings";
const char TEMPORARY_CLEAN_SETTINGS2[] = "-tcs";
const char PID_OPTION[] = "-pid";
const char BLOCK_OPTION[] = "-block";
const char PLUGINPATH_OPTION[] = "-pluginpath";
const char USER_LIBRARY_PATH_OPTION[] = "-user-library-path"; // hidden option for qtcreator.sh
using PluginSpecSet = QVector<PluginSpec *>;
// Helpers for displaying messages. Note that there is no console on Windows.
// Format as <pre> HTML
static inline QString toHtml(const QString &t)
{
QString res = t;
res.replace(QLatin1Char('&'), QLatin1String("&amp;"));
res.replace(QLatin1Char('<'), QLatin1String("&lt;"));
res.replace(QLatin1Char('>'), QLatin1String("&gt;"));
res.insert(0, QLatin1String("<html><pre>"));
res.append(QLatin1String("</pre></html>"));
return res;
}
static void displayHelpText(const QString &t)
{
if (Utils::HostOsInfo::isWindowsHost() && qApp)
QMessageBox::information(nullptr, QLatin1String(Core::Constants::IDE_DISPLAY_NAME), toHtml(t));
else
qWarning("%s", qPrintable(t));
}
static void displayError(const QString &t)
{
if (Utils::HostOsInfo::isWindowsHost() && qApp)
QMessageBox::critical(nullptr, QLatin1String(Core::Constants::IDE_DISPLAY_NAME), t);
else
qCritical("%s", qPrintable(t));
}
static void printVersion(const PluginSpec *coreplugin)
{
QString version;
QTextStream str(&version);
str << '\n' << Core::Constants::IDE_DISPLAY_NAME << ' ' << coreplugin->version()<< " based on Qt " << qVersion() << "\n\n";
PluginManager::formatPluginVersions(str);
str << '\n' << coreplugin->copyright() << '\n';
displayHelpText(version);
}
static void printHelp(const QString &a0)
{
QString help;
QTextStream str(&help);
str << "Usage: " << a0 << fixedOptionsC;
PluginManager::formatOptions(str, OptionIndent, DescriptionIndent);
PluginManager::formatPluginOptions(str, OptionIndent, DescriptionIndent);
displayHelpText(help);
}
QString applicationDirPath(char *arg = nullptr)
{
static QString dir;
if (arg)
dir = QFileInfo(QString::fromLocal8Bit(arg)).dir().absolutePath();
if (QCoreApplication::instance())
return QApplication::applicationDirPath();
return dir;
}
static QString resourcePath()
{
return QDir::cleanPath(applicationDirPath() + '/' + RELATIVE_DATA_PATH);
}
static inline QString msgCoreLoadFailure(const QString &why)
{
return QCoreApplication::translate("Application", "Failed to load core: %1").arg(why);
}
static inline int askMsgSendFailed()
{
return QMessageBox::question(nullptr, QApplication::translate("Application","Could not send message"),
QCoreApplication::translate("Application", "Unable to send command line arguments "
"to the already running instance. It does not appear to "
"be responding. Do you want to start a new instance of "
"%1?").arg(Core::Constants::IDE_DISPLAY_NAME),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Retry,
QMessageBox::Retry);
}
// taken from utils/fileutils.cpp. We cannot use utils here since that depends app_version.h.
static bool copyRecursively(const QString &srcFilePath, const QString &tgtFilePath)
{
QFileInfo srcFileInfo(srcFilePath);
if (srcFileInfo.isDir()) {
QDir targetDir(tgtFilePath);
targetDir.cdUp();
if (!targetDir.mkdir(Utils::FilePath::fromString(tgtFilePath).fileName()))
return false;
QDir sourceDir(srcFilePath);
const QStringList fileNames = sourceDir.entryList
(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
for (const QString &fileName : fileNames) {
const QString newSrcFilePath = srcFilePath + '/' + fileName;
const QString newTgtFilePath = tgtFilePath + '/' + fileName;
if (!copyRecursively(newSrcFilePath, newTgtFilePath))
return false;
}
} else {
if (!QFile::copy(srcFilePath, tgtFilePath))
return false;
}
return true;
}
static inline QStringList getPluginPaths()
{
QStringList rc(QDir::cleanPath(QApplication::applicationDirPath()
+ '/' + RELATIVE_PLUGIN_PATH));
// Local plugin path: <localappdata>/plugins/<ideversion>
// where <localappdata> is e.g.
// "%LOCALAPPDATA%\QtProject\qtcreator" on Windows Vista and later
// "$XDG_DATA_HOME/data/QtProject/qtcreator" or "~/.local/share/data/QtProject/qtcreator" on Linux
// "~/Library/Application Support/QtProject/Qt Creator" on Mac
QString pluginPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost())
pluginPath += QLatin1String("/data");
pluginPath += QLatin1Char('/')
+ QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR)
+ QLatin1Char('/');
pluginPath += QLatin1String(Utils::HostOsInfo::isMacHost() ?
Core::Constants::IDE_DISPLAY_NAME :
Core::Constants::IDE_ID);
pluginPath += QLatin1String("/plugins/");
// Qt Creator X.Y.Z can load plugins from X.Y.(Z-1) etc, so add current and previous
// patch versions
const QString minorVersion = QString::number(IDE_VERSION_MAJOR) + '.'
+ QString::number(IDE_VERSION_MINOR) + '.';
const int minPatchVersion
= qMin(IDE_VERSION_RELEASE,
QVersionNumber::fromString(Core::Constants::IDE_VERSION_COMPAT).microVersion());
for (int patchVersion = IDE_VERSION_RELEASE; patchVersion >= minPatchVersion; --patchVersion)
rc.push_back(pluginPath + minorVersion + QString::number(patchVersion));
return rc;
}
static void setupInstallSettings(QString &installSettingspath)
{
if (!installSettingspath.isEmpty() && !QFileInfo(installSettingspath).isDir()) {
displayError(QString("-installsettingspath \"%0\" needs to be the path where a %1/%2.ini exist.").arg(installSettingspath,
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR), QLatin1String(Core::Constants::IDE_CASED_ID)));
installSettingspath.clear();
}
// Check if the default install settings contain a setting for the actual install settings.
// This can be an absolute path, or a path relative to applicationDirPath().
// The result is interpreted like -settingspath, but for SystemScope
static const char kInstallSettingsKey[] = "Settings/InstallSettings";
QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope,
installSettingspath.isEmpty() ? resourcePath() : installSettingspath);
QSettings installSettings(QSettings::IniFormat, QSettings::UserScope,
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
QLatin1String(Core::Constants::IDE_CASED_ID));
if (installSettings.contains(kInstallSettingsKey)) {
QString installSettingsPath = installSettings.value(kInstallSettingsKey).toString();
if (QDir::isRelativePath(installSettingsPath))
installSettingsPath = applicationDirPath() + '/' + installSettingsPath;
QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, installSettingsPath);
}
}
static QSettings *createUserSettings()
{
return new QSettings(QSettings::IniFormat, QSettings::UserScope,
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
QLatin1String(Core::Constants::IDE_CASED_ID));
}
static inline QSettings *userSettings()
{
QSettings *settings = createUserSettings();
const QString fromVariant = QLatin1String(Core::Constants::IDE_COPY_SETTINGS_FROM_VARIANT_STR);
if (fromVariant.isEmpty())
return settings;
// Copy old settings to new ones:
QFileInfo pathFi = QFileInfo(settings->fileName());
if (pathFi.exists()) // already copied.
return settings;
QDir destDir = pathFi.absolutePath();
if (!destDir.exists())
destDir.mkpath(pathFi.absolutePath());
QDir srcDir = destDir;
srcDir.cdUp();
if (!srcDir.cd(fromVariant))
return settings;
if (srcDir == destDir) // Nothing to copy and no settings yet
return settings;
const QStringList entries = srcDir.entryList();
for (const QString &file : entries) {
const QString lowerFile = file.toLower();
if (lowerFile.startsWith(QLatin1String("profiles.xml"))
|| lowerFile.startsWith(QLatin1String("toolchains.xml"))
|| lowerFile.startsWith(QLatin1String("qtversion.xml"))
|| lowerFile.startsWith(QLatin1String("devices.xml"))
|| lowerFile.startsWith(QLatin1String("debuggers.xml"))
|| lowerFile.startsWith(QLatin1String(Core::Constants::IDE_ID) + "."))
QFile::copy(srcDir.absoluteFilePath(file), destDir.absoluteFilePath(file));
if (file == QLatin1String(Core::Constants::IDE_ID))
copyRecursively(srcDir.absoluteFilePath(file), destDir.absoluteFilePath(file));
}
// Make sure to use the copied settings:
delete settings;
return createUserSettings();
}
static void setHighDpiEnvironmentVariable()
{
if (Utils::HostOsInfo().isMacHost())
return;
std::unique_ptr<QSettings> settings(createUserSettings());
const bool defaultValue = Utils::HostOsInfo().isWindowsHost();
const bool enableHighDpiScaling = settings->value("Core/EnableHighDpiScaling", defaultValue).toBool();
static const char ENV_VAR_QT_DEVICE_PIXEL_RATIO[] = "QT_DEVICE_PIXEL_RATIO";
if (enableHighDpiScaling
&& !qEnvironmentVariableIsSet(ENV_VAR_QT_DEVICE_PIXEL_RATIO) // legacy in 5.6, but still functional
&& !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR")
&& !qEnvironmentVariableIsSet("QT_SCALE_FACTOR")
&& !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
// work around QTBUG-80934
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::Round);
#endif
}
}
void loadFonts()
{
const QDir dir(resourcePath() + "/fonts/");
const QFileInfoList fonts = dir.entryInfoList(QStringList("*.ttf"), QDir::Files);
for (const QFileInfo &fileInfo : fonts)
QFontDatabase::addApplicationFont(fileInfo.absoluteFilePath());
}
struct Options
{
QString settingsPath;
QString installSettingsPath;
QStringList customPluginPaths;
// list of arguments that were handled and not passed to the application or plugin manager
QStringList preAppArguments;
// list of arguments to be passed to the application or plugin manager
std::vector<char *> appArguments;
Utils::optional<QString> userLibraryPath;
bool hasTestOption = false;
bool wantsCleanSettings = false;
};
Options parseCommandLine(int argc, char *argv[])
{
Options options;
auto it = argv;
const auto end = argv + argc;
while (it != end) {
const auto arg = QString::fromLocal8Bit(*it);
const bool hasNext = it + 1 != end;
const auto nextArg = hasNext ? QString::fromLocal8Bit(*(it + 1)) : QString();
if (arg == SETTINGS_OPTION && hasNext) {
++it;
options.settingsPath = QDir::fromNativeSeparators(nextArg);
options.preAppArguments << arg << nextArg;
} else if (arg == INSTALL_SETTINGS_OPTION && hasNext) {
++it;
options.installSettingsPath = QDir::fromNativeSeparators(nextArg);
options.preAppArguments << arg << nextArg;
} else if (arg == PLUGINPATH_OPTION && hasNext) {
++it;
options.customPluginPaths += QDir::fromNativeSeparators(nextArg);
options.preAppArguments << arg << nextArg;
} else if (arg == USER_LIBRARY_PATH_OPTION && hasNext) {
++it;
options.userLibraryPath = nextArg;
options.preAppArguments << arg << nextArg;
} else if (arg == TEMPORARY_CLEAN_SETTINGS1 || arg == TEMPORARY_CLEAN_SETTINGS2) {
options.wantsCleanSettings = true;
options.preAppArguments << arg;
} else { // arguments that are still passed on to the application
if (arg == TEST_OPTION)
options.hasTestOption = true;
options.appArguments.push_back(*it);
}
++it;
}
return options;
}
class Restarter
{
public:
Restarter(int argc, char *argv[])
{
Q_UNUSED(argc)
m_executable = QString::fromLocal8Bit(argv[0]);
m_workingPath = QDir::currentPath();
}
void setArguments(const QStringList &args) { m_args = args; }
QStringList arguments() const { return m_args; }
int restartOrExit(int exitCode)
{
return qApp->property("restart").toBool() ? restart(exitCode) : exitCode;
}
int restart(int exitCode)
{
QProcess::startDetached(m_executable, m_args, m_workingPath);
return exitCode;
}
private:
QString m_executable;
QStringList m_args;
QString m_workingPath;
};
QStringList lastSessionArgument()
{
// using insider information here is not particularly beautiful, anyhow
const bool hasProjectExplorer = Utils::anyOf(PluginManager::plugins(),
Utils::equal(&PluginSpec::name,
QString("ProjectExplorer")));
return hasProjectExplorer ? QStringList({"-lastsession"}) : QStringList();
}
int main(int argc, char **argv)
{
Restarter restarter(argc, argv);
Utils::Environment::systemEnvironment(); // cache system environment before we do any changes
// Manually determine various command line options
// We can't use the regular way of the plugin manager,
// because settings can change the way plugin manager behaves
Options options = parseCommandLine(argc, argv);
applicationDirPath(argv[0]);
if (options.userLibraryPath) {
if ((*options.userLibraryPath).isEmpty()) {
Utils::Environment::modifySystemEnvironment(
{{"LD_LIBRARY_PATH", "", Utils::EnvironmentItem::Unset}});
} else {
Utils::Environment::modifySystemEnvironment(
{{"LD_LIBRARY_PATH", *options.userLibraryPath, Utils::EnvironmentItem::SetEnabled}});
}
}
#ifdef Q_OS_WIN
if (!qEnvironmentVariableIsSet("QT_OPENGL"))
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
if (qEnvironmentVariableIsSet("QTCREATOR_DISABLE_NATIVE_MENUBAR")
|| qgetenv("XDG_CURRENT_DESKTOP").startsWith("Unity")) {
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
}
Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX");
#ifdef Q_OS_MAC
// 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);
setrlimit(RLIMIT_NOFILE, &rl);
#endif
QScopedPointer<Utils::TemporaryDirectory> temporaryCleanSettingsDir;
if (options.settingsPath.isEmpty() && (options.hasTestOption || options.wantsCleanSettings)) {
temporaryCleanSettingsDir.reset(new Utils::TemporaryDirectory("qtc-test-settings"));
if (!temporaryCleanSettingsDir->isValid())
return 1;
options.settingsPath = temporaryCleanSettingsDir->path();
}
if (!options.settingsPath.isEmpty())
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, options.settingsPath);
// Must be done before any QSettings class is created
QSettings::setDefaultFormat(QSettings::IniFormat);
setupInstallSettings(options.installSettingsPath);
// plugin manager takes control of this settings object
setHighDpiEnvironmentVariable();
SharedTools::QtSingleApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
int numberofArguments = static_cast<int>(options.appArguments.size());
SharedTools::QtSingleApplication app((QLatin1String(Core::Constants::IDE_DISPLAY_NAME)),
numberofArguments,
options.appArguments.data());
QCoreApplication::setApplicationName(Core::Constants::IDE_CASED_ID);
QCoreApplication::setApplicationVersion(QLatin1String(Core::Constants::IDE_VERSION_LONG));
QCoreApplication::setOrganizationName(QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR));
QGuiApplication::setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
const QStringList pluginArguments = app.arguments();
/*Initialize global settings and resetup install settings with QApplication::applicationDirPath */
setupInstallSettings(options.installSettingsPath);
QSettings *settings = userSettings();
QSettings *globalSettings = new QSettings(QSettings::IniFormat, QSettings::SystemScope,
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
QLatin1String(Core::Constants::IDE_CASED_ID));
loadFonts();
if (Utils::HostOsInfo().isWindowsHost()
&& !qFuzzyCompare(qApp->devicePixelRatio(), 1.0)
&& QApplication::style()->objectName().startsWith(
QLatin1String("windows"), Qt::CaseInsensitive)) {
QApplication::setStyle(QLatin1String("fusion"));
}
const int threadCount = QThreadPool::globalInstance()->maxThreadCount();
QThreadPool::globalInstance()->setMaxThreadCount(qMax(4, 2 * threadCount));
const QString libexecPath = QCoreApplication::applicationDirPath()
+ '/' + RELATIVE_LIBEXEC_PATH;
#ifdef ENABLE_QT_BREAKPAD
QtSystemExceptionHandler systemExceptionHandler(libexecPath);
#else
// Display a backtrace once a serious signal is delivered (Linux only).
CrashHandlerSetup setupCrashHandler(Core::Constants::IDE_DISPLAY_NAME,
CrashHandlerSetup::EnableRestart, libexecPath);
#endif
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
app.setAttribute(Qt::AA_DisableWindowContextHelpButton);
PluginManager pluginManager;
PluginManager::setPluginIID(QLatin1String("org.qt-project.Qt.QtCreatorPlugin"));
PluginManager::setGlobalSettings(globalSettings);
PluginManager::setSettings(settings);
QTranslator translator;
QTranslator qtTranslator;
QStringList uiLanguages = QLocale::system().uiLanguages();
QString overrideLanguage = settings->value(QLatin1String("General/OverrideLanguage")).toString();
if (!overrideLanguage.isEmpty())
uiLanguages.prepend(overrideLanguage);
const QString &creatorTrPath = resourcePath() + "/translations";
for (QString locale : qAsConst(uiLanguages)) {
locale = QLocale(locale).name();
if (translator.load("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;
}
translator.load(QString()); // unload()
} 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;
}
}
app.setDesktopFileName("org.qt-project.qtcreator.desktop");
// Make sure we honor the system's proxy settings
QNetworkProxyFactory::setUseSystemConfiguration(true);
// Load
const QStringList pluginPaths = getPluginPaths() + options.customPluginPaths;
PluginManager::setPluginPaths(pluginPaths);
QMap<QString, QString> foundAppOptions;
if (pluginArguments.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);
appOptions.insert(QLatin1String(PID_OPTION), true);
appOptions.insert(QLatin1String(BLOCK_OPTION), false);
QString errorMessage;
if (!PluginManager::parseOptions(pluginArguments, appOptions, &foundAppOptions, &errorMessage)) {
displayError(errorMessage);
printHelp(QFileInfo(app.applicationFilePath()).baseName());
return -1;
}
}
restarter.setArguments(options.preAppArguments + PluginManager::argumentsForRestart()
+ lastSessionArgument());
const PluginSpecSet plugins = PluginManager::plugins();
PluginSpec *coreplugin = nullptr;
for (PluginSpec *spec : plugins) {
if (spec->name() == QLatin1String(corePluginNameC)) {
coreplugin = spec;
break;
}
}
if (!coreplugin) {
QString nativePaths = QDir::toNativeSeparators(pluginPaths.join(QLatin1Char(',')));
const QString reason = QCoreApplication::translate("Application", "Could not find Core plugin in %1").arg(nativePaths);
displayError(msgCoreLoadFailure(reason));
return 1;
}
if (!coreplugin->isEffectivelyEnabled()) {
const QString reason = QCoreApplication::translate("Application", "Core plugin is disabled.");
displayError(msgCoreLoadFailure(reason));
return 1;
}
if (coreplugin->hasError()) {
displayError(msgCoreLoadFailure(coreplugin->errorString()));
return 1;
}
if (foundAppOptions.contains(QLatin1String(VERSION_OPTION))) {
printVersion(coreplugin);
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());
return 0;
}
qint64 pid = -1;
if (foundAppOptions.contains(QLatin1String(PID_OPTION))) {
QString pidString = foundAppOptions.value(QLatin1String(PID_OPTION));
bool pidOk;
qint64 tmpPid = pidString.toInt(&pidOk);
if (pidOk)
pid = tmpPid;
}
bool isBlock = foundAppOptions.contains(QLatin1String(BLOCK_OPTION));
if (app.isRunning() && (pid != -1 || isBlock
|| foundAppOptions.contains(QLatin1String(CLIENT_OPTION)))) {
app.setBlock(isBlock);
if (app.sendMessage(PluginManager::serializedArguments(), 5000 /*timeout*/, pid))
return 0;
// Message could not be send, maybe it was in the process of quitting
if (app.isRunning(pid)) {
// Nah app is still running, ask the user
int button = askMsgSendFailed();
while (button == QMessageBox::Retry) {
if (app.sendMessage(PluginManager::serializedArguments(), 5000 /*timeout*/, pid))
return 0;
if (!app.isRunning(pid)) // App quit while we were trying so start a new creator
button = QMessageBox::Yes;
else
button = askMsgSendFailed();
}
if (button == QMessageBox::No)
return -1;
}
}
PluginManager::checkForProblematicPlugins();
PluginManager::loadPlugins();
if (coreplugin->hasError()) {
displayError(msgCoreLoadFailure(coreplugin->errorString()));
return 1;
}
// Set up remote arguments.
QObject::connect(&app, &SharedTools::QtSingleApplication::messageReceived,
&pluginManager, &PluginManager::remoteArguments);
QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(),
SLOT(fileOpenRequest(QString)));
// shutdown plugin manager on the exit
QObject::connect(&app, &QCoreApplication::aboutToQuit, &pluginManager, &PluginManager::shutdown);
return restarter.restartOrExit(app.exec());
}