File indexing completed on 2024-04-21 04:36:22
0001 /* 0002 SPDX-FileCopyrightText: 2003-2009 Alexander Dymo <adymo@kdevelop.org> 0003 SPDX-FileCopyrightText: 2007 Ralf Habacker <Ralf.Habacker@freenet.de> 0004 SPDX-FileCopyrightText: 2006-2007 Matt Rogers <mattr@kde.org> 0005 SPDX-FileCopyrightText: 2006-2007 Hamish Rodda <rodda@kde.org> 0006 SPDX-FileCopyrightText: 2005-2007 Adam Treat <treat@kde.org> 0007 SPDX-FileCopyrightText: 2003-2007 Jens Dagerbo <jens.dagerbo@swipnet.se> 0008 SPDX-FileCopyrightText: 2001-2002 Bernd Gehrmann <bernd@mail.berlios.de> 0009 SPDX-FileCopyrightText: 2001-2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> 0010 SPDX-FileCopyrightText: 2003 Roberto Raggi <roberto@kdevelop.org> 0011 SPDX-FileCopyrightText: 2010 Niko Sams <niko.sams@gmail.com> 0012 SPDX-FileCopyrightText: 2015 Kevin Funk <kfunk@kde.org> 0013 0014 SPDX-License-Identifier: LGPL-2.0-or-later 0015 */ 0016 0017 #include "config-kdevelop.h" 0018 #include "kdevelop_version.h" 0019 0020 #include "urlinfo.h" 0021 0022 #include <KLocalizedString> 0023 #include <Kdelibs4ConfigMigrator> 0024 #include <KAboutData> 0025 #include <KCrash> 0026 0027 #include <QApplication> 0028 #include <QElapsedTimer> 0029 #include <QCommandLineParser> 0030 #include <QCommandLineOption> 0031 #include <QFileInfo> 0032 #include <QProcessEnvironment> 0033 #include <QSessionManager> 0034 #include <QTextStream> 0035 #include <QDBusInterface> 0036 #include <QDBusReply> 0037 0038 #include <QQuickWindow> 0039 0040 #include <shell/core.h> 0041 #include <shell/mainwindow.h> 0042 #include <shell/projectcontroller.h> 0043 #include <shell/documentcontroller.h> 0044 #include <shell/plugincontroller.h> 0045 #include <shell/sessioncontroller.h> 0046 #include <shell/runcontroller.h> 0047 #include <shell/launchconfiguration.h> 0048 #include <shell/session.h> 0049 #include <interfaces/ilauncher.h> 0050 #include <interfaces/iproject.h> 0051 #include <interfaces/launchconfigurationtype.h> 0052 #include <util/path.h> 0053 #include <debug.h> 0054 0055 #include "kdevideextension.h" 0056 #if KDEVELOP_SINGLE_APP 0057 #include "qtsingleapplication.h" 0058 #endif 0059 0060 #include <iostream> 0061 0062 #ifdef Q_OS_MAC 0063 #include <CoreFoundation/CoreFoundation.h> 0064 #endif 0065 0066 using namespace KDevelop; 0067 0068 namespace { 0069 0070 #if KDEVELOP_SINGLE_APP 0071 QString serializeOpenFilesMessage(const QVector<UrlInfo> &infos) 0072 { 0073 QByteArray message; 0074 QDataStream stream(&message, QIODevice::WriteOnly); 0075 stream << QByteArrayLiteral("open"); 0076 stream << infos; 0077 return QString::fromLatin1(message.toHex()); 0078 } 0079 #endif 0080 0081 void openFiles(const QVector<UrlInfo>& infos) 0082 { 0083 for (const UrlInfo& info : infos) { 0084 if (!ICore::self()->documentController()->openDocument(info.url, info.cursor)) { 0085 qCWarning(APP) << i18n("Could not open %1", info.url.toDisplayString(QUrl::PreferLocalFile)); 0086 } 0087 } 0088 } 0089 0090 } 0091 0092 class KDevelopApplication: 0093 #if KDEVELOP_SINGLE_APP 0094 public SharedTools::QtSingleApplication 0095 #else 0096 public QApplication 0097 #endif 0098 { 0099 Q_OBJECT 0100 public: 0101 explicit KDevelopApplication(int &argc, char **argv, bool GUIenabled = true) 0102 #if KDEVELOP_SINGLE_APP 0103 : SharedTools::QtSingleApplication(QStringLiteral("KDevelop"), argc, argv) 0104 #else 0105 : QApplication(argc, argv, GUIenabled) 0106 #endif 0107 { 0108 Q_UNUSED(GUIenabled); 0109 connect(this, &QGuiApplication::saveStateRequest, this, &KDevelopApplication::saveState); 0110 } 0111 0112 #if KDEVELOP_SINGLE_APP 0113 public Q_SLOTS: 0114 void remoteArguments(const QString &message, QObject *socket) 0115 { 0116 Q_UNUSED(socket); 0117 0118 QByteArray ba = QByteArray::fromHex(message.toLatin1()); 0119 QDataStream stream(ba); 0120 QByteArray command; 0121 stream >> command; 0122 0123 qCDebug(APP) << "Received remote command: " << command; 0124 0125 if (command == "open") { 0126 QVector<UrlInfo> infos; 0127 stream >> infos; 0128 0129 QVector<UrlInfo> files, directories; 0130 for (const auto& info : infos) 0131 if (info.isDirectory()) 0132 directories << info; 0133 else 0134 files << info; 0135 0136 openFiles(files); 0137 for(const auto &urlinfo : directories) 0138 ICore::self()->projectController()->openProjectForUrl(urlinfo.url); 0139 } else { 0140 qCWarning(APP) << "Unknown remote command: " << command; 0141 } 0142 } 0143 0144 void fileOpenRequested(const QString &file) 0145 { 0146 openFiles({UrlInfo(file)}); 0147 } 0148 #endif 0149 0150 private Q_SLOTS: 0151 void saveState( QSessionManager& sm ) { 0152 if (KDevelop::Core::self() && KDevelop::Core::self()->sessionController()) { 0153 const auto activeSession = KDevelop::Core::self()->sessionController()->activeSession(); 0154 if (!activeSession) { 0155 qCWarning(APP) << "No active session, can't save state"; 0156 return; 0157 } 0158 0159 const QString x11SessionId = sm.sessionId() + QLatin1Char('_') + sm.sessionKey(); 0160 QString kdevelopSessionId = activeSession->id().toString(); 0161 sm.setRestartCommand({ 0162 QCoreApplication::applicationFilePath(), 0163 QStringLiteral("-session"), 0164 x11SessionId, 0165 QStringLiteral("-s"), 0166 kdevelopSessionId 0167 }); 0168 } 0169 } 0170 }; 0171 0172 /// Tries to find a session identified by @p data in @p sessions. 0173 /// The @p data may be either a session's name or a string-representation of its UUID. 0174 /// @return pointer to the session or NULL if nothing appropriate has been found 0175 static const KDevelop::SessionInfo* findSessionInList( const SessionInfos& sessions, const QString& data ) 0176 { 0177 // We won't search a session without input data, since that could lead to false-positives 0178 // with unnamed sessions 0179 if( data.isEmpty() ) 0180 return nullptr; 0181 0182 for( auto it = sessions.constBegin(); it != sessions.constEnd(); ++it ) { 0183 if ( ( it->name == data ) || ( it->uuid.toString() == data ) ) { 0184 const KDevelop::SessionInfo& sessionRef = *it; 0185 return &sessionRef; 0186 } 0187 } 0188 return nullptr; 0189 } 0190 0191 /// Tries to find sessions containing project @p projectUrl in @p sessions. 0192 static const KDevelop::SessionInfos findSessionsWithProject(const SessionInfos& sessions, const QUrl& projectUrl) 0193 { 0194 if (!projectUrl.isValid()) 0195 return {}; 0196 0197 KDevelop::SessionInfos infos; 0198 for (auto& session : sessions) { 0199 if (session.projects.contains(projectUrl)) { 0200 infos << session; 0201 } 0202 } 0203 return infos; 0204 } 0205 0206 /// Performs a DBus call to open the given @p files in the running kdev instance identified by @p pid 0207 /// Returns the exit status 0208 static int openFilesInRunningInstance(const QVector<UrlInfo>& files, qint64 pid) 0209 { 0210 const QString service = QStringLiteral("org.kdevelop.kdevelop-%1").arg(pid); 0211 QDBusInterface iface(service, QStringLiteral("/org/kdevelop/DocumentController"), QStringLiteral("org.kdevelop.DocumentController")); 0212 0213 QStringList urls; 0214 bool errors_occurred = false; 0215 for (const UrlInfo& file : files) { 0216 QDBusReply<bool> result = iface.call(QStringLiteral("openDocumentSimple"), file.url.toString(), file.cursor.line(), file.cursor.column()); 0217 if ( ! result.value() ) { 0218 QTextStream err(stderr); 0219 err << i18n("Could not open file '%1'.", file.url.toDisplayString(QUrl::PreferLocalFile)) << "\n"; 0220 errors_occurred = true; 0221 } 0222 } 0223 // make the window visible 0224 QDBusMessage makeVisible = QDBusMessage::createMethodCall( service, QStringLiteral("/kdevelop/MainWindow"), QStringLiteral("org.kdevelop.MainWindow"), 0225 QStringLiteral("ensureVisible") ); 0226 QDBusConnection::sessionBus().asyncCall( makeVisible ); 0227 return errors_occurred; 0228 } 0229 0230 /// Performs a DBus call to open the given @p files in the running kdev instance identified by @p pid 0231 /// Returns the exit status 0232 static int openProjectInRunningInstance(const QVector<UrlInfo>& paths, qint64 pid) 0233 { 0234 const QString service = QStringLiteral("org.kdevelop.kdevelop-%1").arg(pid); 0235 QDBusInterface iface(service, QStringLiteral("/org/kdevelop/ProjectController"), QStringLiteral("org.kdevelop.ProjectController")); 0236 int errors = 0; 0237 0238 for (const UrlInfo& path : paths) { 0239 QDBusReply<void> result = iface.call(QStringLiteral("openProjectForUrl"), path.url.toString()); 0240 if ( !result.isValid() ) { 0241 QTextStream err(stderr); 0242 err << i18n("Could not open project '%1': %2", path.url.toDisplayString(QUrl::PreferLocalFile), result.error().message()) << "\n"; 0243 ++errors; 0244 } 0245 } 0246 // make the window visible 0247 QDBusMessage makeVisible = QDBusMessage::createMethodCall( service, QStringLiteral("/kdevelop/MainWindow"), QStringLiteral("org.kdevelop.MainWindow"), 0248 QStringLiteral("ensureVisible") ); 0249 QDBusConnection::sessionBus().asyncCall( makeVisible ); 0250 return errors; 0251 } 0252 0253 /// Gets the PID of a running KDevelop instance, eventually asking the user if there is more than one. 0254 /// Returns -1 in case there are no running sessions. 0255 static qint64 getRunningSessionPid() 0256 { 0257 SessionInfos candidates; 0258 const auto availableSessionInfos = KDevelop::SessionController::availableSessionInfos(); 0259 for (const KDevelop::SessionInfo& si : availableSessionInfos) { 0260 if( KDevelop::SessionController::isSessionRunning(si.uuid.toString()) ) { 0261 candidates << si; 0262 } 0263 } 0264 if ( candidates.isEmpty() ) { 0265 return -1; 0266 } 0267 0268 QString sessionUuid; 0269 if ( candidates.size() == 1 ) { 0270 sessionUuid = candidates.first().uuid.toString(); 0271 } 0272 else { 0273 const QString title = i18n("Select the session to open the document in"); 0274 sessionUuid = KDevelop::SessionController::showSessionChooserDialog(title, true); 0275 } 0276 return KDevelop::SessionController::sessionRunInfo(sessionUuid).holderPid; 0277 } 0278 0279 static QString findSessionId(const SessionInfos& availableSessionInfos, const QString& session) 0280 { 0281 //If there is a session and a project with the same name, always open the session 0282 //regardless of the order encountered 0283 QString projectAsSession; 0284 for (const KDevelop::SessionInfo& si : availableSessionInfos) { 0285 if ( session == si.name || session == si.uuid.toString() ) { 0286 return si.uuid.toString(); 0287 } else if (projectAsSession.isEmpty()) { 0288 for (const QUrl& k : si.projects) { 0289 QString fn(k.fileName()); 0290 fn = fn.left(fn.indexOf(QLatin1Char('.'))); 0291 if ( session == fn ) { 0292 projectAsSession = si.uuid.toString(); 0293 } 0294 } 0295 } 0296 } 0297 0298 if (projectAsSession.isEmpty()) { 0299 QTextStream qerr(stderr); 0300 qerr << QLatin1Char('\n') << i18n("Cannot open unknown session %1. See `--list-sessions` switch for available sessions or use `-n` to create a new one.", 0301 session) << QLatin1Char('\n'); 0302 } 0303 return projectAsSession; 0304 } 0305 0306 static qint64 findSessionPid(const QString &sessionId) 0307 { 0308 KDevelop::SessionRunInfo sessionInfo = KDevelop::SessionController::sessionRunInfo( sessionId ); 0309 return sessionInfo.holderPid; 0310 } 0311 0312 int main( int argc, char *argv[] ) 0313 { 0314 QElapsedTimer timer; 0315 timer.start(); 0316 0317 // If possible, use the Software backend for QQuickWidget (currently used in the 0318 // welcome page plugin). This means we don't need OpenGL at all, avoiding issues 0319 // like https://bugs.kde.org/show_bug.cgi?id=386527. 0320 QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software); 0321 0322 // Useful for valgrind runs, just `export KDEV_DISABLE_JIT=1` 0323 if (qEnvironmentVariableIsSet("KDEV_DISABLE_JIT")) { 0324 qputenv("KDEV_DISABLE_WELCOMEPAGE", "1"); 0325 qputenv("QT_ENABLE_REGEXP_JIT", "0"); 0326 } 0327 0328 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 0329 QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); 0330 0331 #ifdef Q_OS_MAC 0332 CFBundleRef mainBundle = CFBundleGetMainBundle(); 0333 if (mainBundle) { 0334 // get the application's Info Dictionary. For app bundles this would live in the bundle's Info.plist, 0335 // for regular executables it is obtained in another way. 0336 CFMutableDictionaryRef infoDict = (CFMutableDictionaryRef) CFBundleGetInfoDictionary(mainBundle); 0337 if (infoDict) { 0338 // Try to prevent App Nap on OS X. This can be tricky in practice, at least in 10.9 . 0339 CFDictionarySetValue(infoDict, CFSTR("NSAppSleepDisabled"), kCFBooleanTrue); 0340 CFDictionarySetValue(infoDict, CFSTR("NSSupportsAutomaticTermination"), kCFBooleanFalse); 0341 } 0342 } 0343 #endif 0344 0345 //we can't use KCmdLineArgs as it doesn't allow arguments for the debugee 0346 //so lookup the --debug switch and eat everything behind by decrementing argc 0347 //debugArgs is filled with args after --debug <debuger> 0348 QStringList debugArgs; 0349 QString debugeeName; 0350 { 0351 bool debugFound = false; 0352 int c = argc; 0353 for (int i=0; i < c; ++i) { 0354 if (debugFound) { 0355 debugArgs << QString::fromUtf8(argv[i]); 0356 } else if ((qstrcmp(argv[i], "--debug") == 0) || (qstrcmp(argv[i], "-d") == 0)) { 0357 if (argc > (i + 1)) { 0358 i++; 0359 } 0360 argc = i + 1; 0361 debugFound = true; 0362 } else if (QByteArray(argv[i]).startsWith("--debug=")) { 0363 argc = i + 1; 0364 debugFound = true; 0365 } 0366 } 0367 } 0368 0369 KDevelopApplication app(argc, argv); 0370 // Prevent SIGPIPE, then "ICE default IO error handler doing an exit(), pid = <PID>, errno = 32" 0371 // crash when the first event loop starts at least 60 seconds after KDevelop launch. This can 0372 // happen during a Debug Launch of KDevelop from KDevelop, especially if a breakpoint is hit 0373 // before any event loop is entered. 0374 QCoreApplication::processEvents(); 0375 0376 KLocalizedString::setApplicationDomain("kdevelop"); 0377 0378 KAboutData aboutData(QStringLiteral("kdevelop"), i18n("KDevelop"), 0379 QStringLiteral(KDEVELOP_VERSION_STRING " (" RELEASE_SERVICE_VERSION_STRING ")"), 0380 i18n("The KDevelop Integrated Development Environment"), KAboutLicense::GPL, 0381 i18n("Copyright 1999-%1, The KDevelop developers", QStringLiteral("2023")), QString(), 0382 QStringLiteral("https://www.kdevelop.org/")); 0383 aboutData.setDesktopFileName(QStringLiteral("org.kde.kdevelop")); 0384 aboutData.addAuthor( i18n("Kevin Funk"), i18n( "Co-maintainer, C++/Clang, QA, Windows Support, Performance, Website" ), QStringLiteral("kfunk@kde.org") ); 0385 aboutData.addAuthor( i18n("Sven Brauch"), i18n( "Co-maintainer, AppImage, Python Support, User Interface improvements" ), QStringLiteral("svenbrauch@gmail.com") ); 0386 aboutData.addAuthor( i18n("Aleix Pol Gonzalez"), i18n( "CMake Support, Run Support, Kross Support" ), QStringLiteral("aleixpol@kde.org") ); 0387 aboutData.addAuthor( i18n("Milian Wolff"), i18n( "C++/Clang, Generic manager, Webdevelopment Plugins, Snippets, Performance" ), QStringLiteral("mail@milianw.de") ); 0388 aboutData.addAuthor( i18n("Olivier JG"), i18n( "C++/Clang, DUChain, Bug Fixes" ), QStringLiteral("olivier.jg@gmail.com") ); 0389 aboutData.addAuthor( i18n("Andreas Pakulat"), i18n( "Architecture, VCS Support, Project Management Support, QMake Projectmanager" ), QStringLiteral("apaku@gmx.de") ); 0390 aboutData.addAuthor( i18n("Alexander Dymo"), i18n( "Architecture, Sublime UI, Ruby support" ), QStringLiteral("adymo@kdevelop.org") ); 0391 aboutData.addAuthor( i18n("David Nolden"), i18n( "Definition-Use Chain, C++ Support, Code Navigation, Code Completion, Coding Assistance, Refactoring" ), QStringLiteral("david.nolden.kdevelop@art-master.de") ); 0392 aboutData.addAuthor( i18n("Vladimir Prus"), i18n( "GDB integration" ), QStringLiteral("ghost@cs.msu.su") ); 0393 aboutData.addAuthor( i18n("Hamish Rodda"), i18n( "Text editor integration, definition-use chain" ), QStringLiteral("rodda@kde.org") ); 0394 aboutData.addAuthor( i18n("Amilcar do Carmo Lucas"), i18n( "Website admin, API documentation, Doxygen and autoproject patches" ), QStringLiteral("amilcar@kdevelop.org") ); 0395 aboutData.addAuthor( i18n("Niko Sams"), i18n( "GDB integration, Webdevelopment Plugins" ), QStringLiteral("niko.sams@gmail.com") ); 0396 aboutData.addAuthor( i18n("Friedrich W. H. Kossebau"), QString(), QStringLiteral("kossebau@kde.org") ); 0397 0398 aboutData.addCredit( i18n("Matt Rogers"), QString(), QStringLiteral("mattr@kde.org")); 0399 aboutData.addCredit( i18n("Cédric Pasteur"), i18n("astyle and indent support"), QStringLiteral("cedric.pasteur@free.fr") ); 0400 aboutData.addCredit( i18n("Evgeniy Ivanov"), i18n("Distributed VCS, Git, Mercurial"), QStringLiteral("powerfox@kde.ru") ); 0401 // QTest integration is separate in playground currently. 0402 //aboutData.addCredit( i18n("Manuel Breugelmanns"), i18n( "Veritas, QTest integration"), "mbr.nxi@gmail.com" ); 0403 aboutData.addCredit( i18n("Robert Gruber") , i18n( "SnippetPart, debugger and usability patches" ), QStringLiteral("rgruber@users.sourceforge.net") ); 0404 aboutData.addCredit( i18n("Dukju Ahn"), i18n( "Subversion plugin, Custom Make Manager, Overall improvements" ), QStringLiteral("dukjuahn@gmail.com") ); 0405 aboutData.addCredit( i18n("Harald Fernengel"), i18n( "Ported to Qt 3, patches, valgrind, diff and perforce support" ), QStringLiteral("harry@kdevelop.org") ); 0406 aboutData.addCredit( i18n("Roberto Raggi"), i18n( "C++ parser" ), QStringLiteral("roberto@kdevelop.org") ); 0407 aboutData.addCredit( i18n("The KWrite authors"), i18n( "Kate editor component" ), QStringLiteral("kwrite-devel@kde.org") ); 0408 aboutData.addCredit( i18n("Nokia Corporation/Qt Software"), i18n( "Designer code" ), QStringLiteral("qt-info@nokia.com") ); 0409 0410 aboutData.addCredit( i18n("Contributors to older versions:"), QString(), QString() ); 0411 aboutData.addCredit( i18n("Bernd Gehrmann"), i18n( "Initial idea, basic architecture, much initial source code" ), QStringLiteral("bernd@kdevelop.org") ); 0412 aboutData.addCredit( i18n("Caleb Tennis"), i18n( "KTabBar, bugfixes" ), QStringLiteral("caleb@aei-tech.com") ); 0413 aboutData.addCredit( i18n("Richard Dale"), i18n( "Java & Objective C support" ), QStringLiteral("Richard_Dale@tipitina.demon.co.uk") ); 0414 aboutData.addCredit( i18n("John Birch"), i18n( "Debugger frontend" ), QStringLiteral("jbb@kdevelop.org") ); 0415 aboutData.addCredit( i18n("Sandy Meier"), i18n( "PHP support, context menu stuff" ), QStringLiteral("smeier@kdevelop.org") ); 0416 aboutData.addCredit( i18n("Kurt Granroth"), i18n( "KDE application templates" ), QStringLiteral("kurth@granroth.org") ); 0417 aboutData.addCredit( i18n("Ian Reinhart Geiser"), i18n( "Dist part, bash support, application templates" ), QStringLiteral("geiseri@yahoo.com") ); 0418 aboutData.addCredit( i18n("Matthias Hoelzer-Kluepfel"), i18n( "Several components, htdig indexing" ), QStringLiteral("hoelzer@kde.org") ); 0419 aboutData.addCredit( i18n("Victor Roeder"), i18n( "Help with Automake manager and persistent class store" ), QStringLiteral("victor_roeder@gmx.de") ); 0420 aboutData.addCredit( i18n("Simon Hausmann"), i18n( "Help with KParts infrastructure" ), QStringLiteral("hausmann@kde.org") ); 0421 aboutData.addCredit( i18n("Oliver Kellogg"), i18n( "Ada support" ), QStringLiteral("okellogg@users.sourceforge.net") ); 0422 aboutData.addCredit( i18n("Jakob Simon-Gaarde"), i18n( "QMake projectmanager" ), QStringLiteral("jsgaarde@tdcspace.dk") ); 0423 aboutData.addCredit( i18n("Falk Brettschneider"), i18n( "MDI modes, QEditor, bugfixes" ), QStringLiteral("falkbr@kdevelop.org") ); 0424 aboutData.addCredit( i18n("Mario Scalas"), i18n( "PartExplorer, redesign of CvsPart, patches, bugs(fixes)" ), QStringLiteral("mario.scalas@libero.it") ); 0425 aboutData.addCredit( i18n("Jens Dagerbo"), i18n( "Replace, Bookmarks, FileList and CTags2 plugins. Overall improvements and patches" ), QStringLiteral("jens.dagerbo@swipnet.se") ); 0426 aboutData.addCredit( i18n("Julian Rockey"), i18n( "Filecreate part and other bits and patches" ), QStringLiteral("linux@jrockey.com") ); 0427 aboutData.addCredit( i18n("Ajay Guleria"), i18n( "ClearCase support" ), QStringLiteral("ajay_guleria@yahoo.com") ); 0428 aboutData.addCredit( i18n("Marek Janukowicz"), i18n( "Ruby support" ), QStringLiteral("child@t17.ds.pwr.wroc.pl") ); 0429 aboutData.addCredit( i18n("Robert Moniot"), i18n( "Fortran documentation" ), QStringLiteral("moniot@fordham.edu") ); 0430 aboutData.addCredit( i18n("Ka-Ping Yee"), i18n( "Python documentation utility" ), QStringLiteral("ping@lfw.org") ); 0431 aboutData.addCredit( i18n("Dimitri van Heesch"), i18n( "Doxygen wizard" ), QStringLiteral("dimitri@stack.nl") ); 0432 aboutData.addCredit( i18n("Hugo Varotto"), i18n( "Fileselector component" ), QStringLiteral("hugo@varotto-usa.com") ); 0433 aboutData.addCredit( i18n("Matt Newell"), i18n( "Fileselector component" ), QStringLiteral("newellm@proaxis.com") ); 0434 aboutData.addCredit( i18n("Daniel Engelschalt"), i18n( "C++ code completion, persistent class store" ), QStringLiteral("daniel.engelschalt@gmx.net") ); 0435 aboutData.addCredit( i18n("Stephane Ancelot"), i18n( "Patches" ), QStringLiteral("sancelot@free.fr") ); 0436 aboutData.addCredit( i18n("Jens Zurheide"), i18n( "Patches" ), QStringLiteral("jens.zurheide@gmx.de") ); 0437 aboutData.addCredit( i18n("Luc Willems"), i18n( "Help with Perl support" ), QStringLiteral("Willems.luc@pandora.be") ); 0438 aboutData.addCredit( i18n("Marcel Turino"), i18n( "Documentation index view" ), QStringLiteral("M.Turino@gmx.de") ); 0439 aboutData.addCredit( i18n("Yann Hodique"), i18n( "Patches" ), QStringLiteral("Yann.Hodique@lifl.fr") ); 0440 aboutData.addCredit( i18n("Tobias Gl\303\244\303\237er") , i18n( "Documentation Finder, qmake projectmanager patches, usability improvements, bugfixes ... " ), QStringLiteral("tobi.web@gmx.de") ); 0441 aboutData.addCredit( i18n("Andreas Koepfle") , i18n( "QMake project manager patches" ), QStringLiteral("koepfle@ti.uni-mannheim.de") ); 0442 aboutData.addCredit( i18n("Sascha Cunz") , i18n( "Cleanup and bugfixes for qEditor, AutoMake and much other stuff" ), QStringLiteral("mail@sacu.de") ); 0443 aboutData.addCredit( i18n("Zoran Karavla"), i18n( "Artwork for the ruby language" ), QStringLiteral("webmaster@the-error.net"), QStringLiteral("http://the-error.net") ); 0444 0445 KAboutData::setApplicationData(aboutData); 0446 // set icon for shells which do not use desktop file metadata 0447 // but without setting replacing an existing icon with an empty one! 0448 QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("kdevelop"), QApplication::windowIcon())); 0449 0450 KCrash::initialize(); 0451 0452 Kdelibs4ConfigMigrator migrator(QStringLiteral("kdevelop")); 0453 migrator.setConfigFiles({QStringLiteral("kdeveloprc")}); 0454 migrator.setUiFiles({QStringLiteral("kdevelopui.rc")}); 0455 migrator.migrate(); 0456 0457 // High DPI support 0458 app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); 0459 0460 qCDebug(APP) << "Startup"; 0461 0462 QCommandLineParser parser; 0463 aboutData.setupCommandLine(&parser); 0464 0465 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("n"), QStringLiteral("new-session")}, 0466 i18n("Open KDevelop with a new session using the given name."), 0467 QStringLiteral("name")}); 0468 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("s"), QStringLiteral("open-session")}, 0469 i18n("Open KDevelop with the given session.\n" 0470 "You can pass either hash or the name of the session."), 0471 QStringLiteral("session")}); 0472 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("rm"), QStringLiteral("remove-session")}, 0473 i18n("Delete the given session.\n" 0474 "You can pass either hash or the name of the session." ), 0475 QStringLiteral("session")}); 0476 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("ps"), QStringLiteral("pick-session")}, 0477 i18n("Shows all available sessions and lets you select one to open.")}); 0478 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("pss"), QStringLiteral("pick-session-shell")}, 0479 i18n("List all available sessions on shell and lets you select one to open.")}); 0480 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("l"), QStringLiteral("list-sessions")}, 0481 i18n("List available sessions and quit.")}); 0482 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("f"), QStringLiteral("fetch")}, 0483 i18n("Open KDevelop and fetch the project from the given <repo url>."), 0484 QStringLiteral("repo url")}); 0485 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("p"), QStringLiteral("project")}, 0486 i18n("Open KDevelop and load the given project. <project> can be either a .kdev4 file or a directory path."), 0487 QStringLiteral("project")}); 0488 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("d"), QStringLiteral("debug")}, 0489 i18n("Start debugging an application in KDevelop with the given debugger.\n" 0490 "The executable that should be debugged must follow - including arguments.\n" 0491 "Example: kdevelop --debug gdb myapp --foo bar"), QStringLiteral("debugger")}); 0492 0493 // this is used by the 'kdevelop!' script to retrieve the pid of a KDEVELOP 0494 // instance. When this is called, then we should just print the PID on the 0495 // standard-output. If a session is specified through open-session, then 0496 // we should return the PID of that session. Otherwise, if only a single 0497 // session is running, then we should just return the PID of that session. 0498 // Otherwise, we should print a command-line session-chooser dialog ("--pss"), 0499 // which only shows the running sessions, and the user can pick one. 0500 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("pid")}}); 0501 0502 parser.addPositionalArgument(QStringLiteral("files"), 0503 i18n( "Files to load, or directories to load as projects" ), QStringLiteral("[FILE[:line[:column]] | DIRECTORY]...")); 0504 0505 // The session-controller needs to arguments to eventually pass them to newly opened sessions 0506 KDevelop::SessionController::setArguments(argc, argv); 0507 0508 parser.process(app); 0509 aboutData.processCommandLine(&parser); 0510 0511 if(parser.isSet(QStringLiteral("list-sessions"))) 0512 { 0513 QTextStream qout(stdout); 0514 qout << QLatin1Char('\n') << ki18n("Available sessions (use '-s HASH' or '-s NAME' to open a specific one):").toString() << QLatin1String("\n\n"); 0515 qout << QStringLiteral("%1").arg(ki18n("Hash").toString(), -38) << '\t' << ki18n("Name: Opened Projects").toString() << QLatin1Char('\n'); 0516 const auto availableSessionInfos = KDevelop::SessionController::availableSessionInfos(); 0517 for (const KDevelop::SessionInfo& si : availableSessionInfos) { 0518 if ( si.name.isEmpty() && si.projects.isEmpty() ) { 0519 continue; 0520 } 0521 qout << si.uuid.toString() << '\t' << si.description; 0522 0523 if(KDevelop::SessionController::isSessionRunning(si.uuid.toString())) 0524 qout << " " << i18n("[running]"); 0525 0526 qout << QLatin1Char('\n'); 0527 } 0528 return 0; 0529 } 0530 0531 // Handle extra arguments, which stand for files to open 0532 QVector<UrlInfo> initialFiles; 0533 QVector<UrlInfo> initialDirectories; 0534 const auto files = parser.positionalArguments(); 0535 for (const QString& file : files) { 0536 const UrlInfo info(file); 0537 if (info.isDirectory()) { 0538 initialDirectories.append(info); 0539 } else { 0540 initialFiles.append(info); 0541 } 0542 } 0543 0544 const auto availableSessionInfos = KDevelop::SessionController::availableSessionInfos(); 0545 0546 if ((!initialFiles.isEmpty() || !initialDirectories.isEmpty()) && !parser.isSet(QStringLiteral("new-session"))) { 0547 #if KDEVELOP_SINGLE_APP 0548 if (app.isRunning()) { 0549 bool success = app.sendMessage(serializeOpenFilesMessage(initialFiles << initialDirectories)); 0550 if (success) { 0551 return 0; 0552 } 0553 } 0554 #else 0555 qint64 pid = -1; 0556 if (parser.isSet(QStringLiteral("open-session"))) { 0557 const QString session = findSessionId(availableSessionInfos, parser.value(QStringLiteral("open-session"))); 0558 if (session.isEmpty()) { 0559 return 1; 0560 } else if (KDevelop::SessionController::isSessionRunning(session)) { 0561 pid = findSessionPid(session); 0562 } 0563 } else { 0564 pid = getRunningSessionPid(); 0565 } 0566 0567 if ( pid > 0 ) { 0568 return openFilesInRunningInstance(initialFiles, pid) + openProjectInRunningInstance(initialDirectories, pid); 0569 } 0570 // else there are no running sessions, and the generated list of files will be opened below. 0571 #endif 0572 } 0573 0574 // if empty, restart kdevelop with last active session, see SessionController::defaultSessionId 0575 QString session; 0576 0577 uint nRunningSessions = 0; 0578 for (const KDevelop::SessionInfo& si : availableSessionInfos) { 0579 if(KDevelop::SessionController::isSessionRunning(si.uuid.toString())) 0580 ++nRunningSessions; 0581 } 0582 0583 // also show the picker dialog when a pid shall be retrieved and multiple 0584 // sessions are running. 0585 if(parser.isSet(QStringLiteral("pss")) || (parser.isSet(QStringLiteral("pid")) && !parser.isSet(QStringLiteral("open-session")) && !parser.isSet(QStringLiteral("ps")) && nRunningSessions > 1)) 0586 { 0587 SessionInfos candidates; 0588 for (const KDevelop::SessionInfo& si : availableSessionInfos) { 0589 if( (!si.name.isEmpty() || !si.projects.isEmpty() || parser.isSet(QStringLiteral("pid"))) && 0590 (!parser.isSet(QStringLiteral("pid")) || KDevelop::SessionController::isSessionRunning(si.uuid.toString()))) 0591 candidates << si; 0592 } 0593 0594 if(candidates.size() == 0) 0595 { 0596 QTextStream qerr(stderr); 0597 qerr << "no session available" << QLatin1Char('\n'); 0598 return 1; 0599 } 0600 0601 if(candidates.size() == 1 && parser.isSet(QStringLiteral("pid"))) 0602 { 0603 session = candidates.constFirst().uuid.toString(); 0604 }else{ 0605 QTextStream qout(stdout); 0606 for(int i = 0; i < candidates.size(); ++i) 0607 qout << "[" << i << "]: " << candidates.at(i).description << QLatin1Char('\n'); 0608 qout.flush(); 0609 0610 int chosen; 0611 std::cin >> chosen; 0612 if(std::cin.good() && (chosen >= 0 && chosen < candidates.size())) 0613 { 0614 session = candidates.at(chosen).uuid.toString(); 0615 }else{ 0616 QTextStream qerr(stderr); 0617 qerr << "invalid selection" << QLatin1Char('\n'); 0618 return 1; 0619 } 0620 } 0621 } 0622 0623 if(parser.isSet(QStringLiteral("ps"))) 0624 { 0625 bool onlyRunning = parser.isSet(QStringLiteral("pid")); 0626 session = KDevelop::SessionController::showSessionChooserDialog(i18n("Select the session you would like to use"), onlyRunning); 0627 if(session.isEmpty()) 0628 return 1; 0629 } 0630 0631 if ( parser.isSet(QStringLiteral("debug")) ) { 0632 if ( debugArgs.isEmpty() ) { 0633 QTextStream qerr(stderr); 0634 qerr << QLatin1Char('\n') << i18nc("@info:shell", "Specify the executable you want to debug.") << QLatin1Char('\n'); 0635 return 1; 0636 } 0637 0638 QFileInfo executableFileInfo(debugArgs.first()); 0639 if (!executableFileInfo.exists()) { 0640 executableFileInfo = QStandardPaths::findExecutable(debugArgs.first()); 0641 if (!executableFileInfo.exists()) { 0642 QTextStream qerr(stderr); 0643 qerr << QLatin1Char('\n') << i18nc("@info:shell", "Specified executable does not exist.") << QLatin1Char('\n'); 0644 return 1; 0645 } 0646 } 0647 0648 debugArgs.first() = executableFileInfo.absoluteFilePath(); 0649 debugeeName = i18n("Debug %1", executableFileInfo.fileName()); 0650 session = debugeeName; 0651 } else if ( parser.isSet(QStringLiteral("new-session")) ) 0652 { 0653 session = parser.value(QStringLiteral("new-session")); 0654 for (const KDevelop::SessionInfo& si : availableSessionInfos) { 0655 if ( session == si.name ) { 0656 QTextStream qerr(stderr); 0657 qerr << QLatin1Char('\n') << i18n("A session with the name %1 exists already. Use the -s switch to open it.", session) << QLatin1Char('\n'); 0658 return 1; 0659 } 0660 } 0661 // session doesn't exist, we can create it 0662 } else if ( parser.isSet(QStringLiteral("open-session")) ) { 0663 session = findSessionId(availableSessionInfos, parser.value(QStringLiteral("open-session"))); 0664 if (session.isEmpty()) { 0665 return 1; 0666 } 0667 } else if ( parser.isSet(QStringLiteral("remove-session")) ) 0668 { 0669 session = parser.value(QStringLiteral("remove-session")); 0670 auto si = findSessionInList(availableSessionInfos, session); 0671 if (!si) { 0672 QTextStream qerr(stderr); 0673 qerr << QLatin1Char('\n') << i18n("No session with the name %1 exists.", session) << QLatin1Char('\n'); 0674 return 1; 0675 } 0676 0677 auto sessionLock = KDevelop::SessionController::tryLockSession(si->uuid.toString()); 0678 if (!sessionLock.lock) { 0679 QTextStream qerr(stderr); 0680 qerr << QLatin1Char('\n') << i18n("Could not lock session %1 for deletion.", session) << QLatin1Char('\n'); 0681 return 1; 0682 } 0683 KDevelop::SessionController::deleteSessionFromDisk(sessionLock.lock); 0684 QTextStream qout(stdout); 0685 qout << QLatin1Char('\n') << i18n("Session with name %1 was successfully removed.", session) << QLatin1Char('\n'); 0686 return 0; 0687 } 0688 0689 if(parser.isSet(QStringLiteral("pid"))) { 0690 if (session.isEmpty()) 0691 { // just pick the first running session 0692 for (const KDevelop::SessionInfo& si : availableSessionInfos) { 0693 if(KDevelop::SessionController::isSessionRunning(si.uuid.toString())) 0694 session = si.uuid.toString(); 0695 } 0696 } 0697 const KDevelop::SessionInfo* sessionData = findSessionInList(availableSessionInfos, session); 0698 0699 if( !sessionData ) { 0700 qCCritical(APP) << "session not given or does not exist"; 0701 return 5; 0702 } 0703 0704 const auto pid = findSessionPid(sessionData->uuid.toString()); 0705 if (pid > 0) { 0706 // Print the PID and we're ready 0707 std::cout << pid << std::endl; 0708 return 0; 0709 } else { 0710 qCCritical(APP) << sessionData->uuid.toString() << sessionData->name << "is not running"; 0711 return 5; 0712 } 0713 } 0714 0715 if (parser.isSet(QStringLiteral("project"))) { 0716 const auto project = parser.value(QStringLiteral("project")); 0717 QFileInfo info(project); 0718 QUrl projectUrl; 0719 if (info.suffix() == QLatin1String("kdev4")) { 0720 projectUrl = QUrl::fromLocalFile(info.absoluteFilePath()); 0721 } else if (info.isDir()) { 0722 QDir dir(info.absoluteFilePath()); 0723 const auto potentialProjectFiles = dir.entryList({QStringLiteral("*.kdev4")}, QDir::Files, QDir::Name); 0724 qCDebug(APP) << "Found these potential project files:" << potentialProjectFiles; 0725 if (!potentialProjectFiles.isEmpty()) { 0726 projectUrl = QUrl::fromLocalFile(dir.absoluteFilePath(potentialProjectFiles.value(0))); 0727 } 0728 } else { 0729 QTextStream qerr(stderr); 0730 qerr << "Invalid project: " << project << " - should be either a path to a .kdev4 file or a directory containing a .kdev4 file"; 0731 return 1; 0732 } 0733 0734 qCDebug(APP) << "Attempting to find a suitable session for project" << projectUrl; 0735 const auto sessionInfos = findSessionsWithProject(availableSessionInfos, projectUrl); 0736 qCDebug(APP) << "Found matching sessions:" << sessionInfos.size(); 0737 if (!sessionInfos.isEmpty()) { 0738 // TODO: If there's more than one match: Allow the user to select which session to open? 0739 qCDebug(APP) << "Attempting to open session:" << sessionInfos.at(0).name; 0740 session = sessionInfos.at(0).uuid.toString(); 0741 } 0742 } 0743 0744 KDevIDEExtension::init(); 0745 0746 qCDebug(APP) << "Attempting to initialize session:" << session; 0747 if(!Core::initialize(Core::Default, session)) 0748 return 5; 0749 0750 // register a DBUS service for this process, so that we can open files in it from other invocations 0751 QDBusConnection::sessionBus().registerService(QStringLiteral("org.kdevelop.kdevelop-%1").arg(app.applicationPid())); 0752 0753 Core* core = Core::self(); 0754 if (!QProcessEnvironment::systemEnvironment().contains(QStringLiteral("KDEV_DISABLE_WELCOMEPAGE"))) { 0755 core->pluginController()->loadPlugin(QStringLiteral("KDevWelcomePage")); 0756 } 0757 0758 const auto fetchUrlStrings = parser.values(QStringLiteral("fetch")); 0759 for (const auto& fetchUrlString : fetchUrlStrings) { 0760 core->projectControllerInternal()->fetchProjectFromUrl(QUrl::fromUserInput(fetchUrlString)); 0761 } 0762 0763 const QString debugStr = QStringLiteral("debug"); 0764 if ( parser.isSet(debugStr) ) { 0765 Q_ASSERT( !debugeeName.isEmpty() ); 0766 QString launchName = debugeeName; 0767 0768 KDevelop::LaunchConfiguration* launch = nullptr; 0769 qCDebug(APP) << launchName; 0770 const auto launchconfigurations = core->runControllerInternal()->launchConfigurationsInternal(); 0771 for (KDevelop::LaunchConfiguration* l : launchconfigurations) { 0772 qCDebug(APP) << l->name(); 0773 if (l->name() == launchName) { 0774 launch = l; 0775 } 0776 } 0777 0778 KDevelop::LaunchConfigurationType *type = nullptr; 0779 const auto launchConfigurationTypes = core->runController()->launchConfigurationTypes(); 0780 for (KDevelop::LaunchConfigurationType* t : launchConfigurationTypes) { 0781 qCDebug(APP) << t->id(); 0782 if (t->id() == QLatin1String("Native Application")) { 0783 type = t; 0784 break; 0785 } 0786 } 0787 if (!type) { 0788 QTextStream qerr(stderr); 0789 qerr << QLatin1Char('\n') << i18n("Cannot find native launch configuration type") << QLatin1Char('\n'); 0790 return 1; 0791 } 0792 0793 if (launch && launch->type()->id() != QLatin1String("Native Application")) launch = nullptr; 0794 if (launch && launch->launcherForMode(debugStr) != parser.value(debugStr)) launch = nullptr; 0795 if (!launch) { 0796 qCDebug(APP) << launchName << "not found, creating a new one"; 0797 QPair<QString,QString> launcher; 0798 launcher.first = debugStr; 0799 const auto typeLaunchers = type->launchers(); 0800 for (KDevelop::ILauncher* l : typeLaunchers) { 0801 if (l->id() == parser.value(debugStr)) { 0802 if (l->supportedModes().contains(debugStr)) { 0803 launcher.second = l->id(); 0804 } 0805 } 0806 } 0807 if (launcher.second.isEmpty()) { 0808 QTextStream qerr(stderr); 0809 qerr << QLatin1Char('\n') << i18n("Cannot find launcher %1", parser.value(debugStr)) << QLatin1Char('\n'); 0810 return 1; 0811 } 0812 KDevelop::ILaunchConfiguration* ilaunch = core->runController()->createLaunchConfiguration(type, launcher, nullptr, launchName); 0813 launch = static_cast<KDevelop::LaunchConfiguration*>(ilaunch); 0814 } 0815 0816 type->configureLaunchFromCmdLineArguments(launch->config(), debugArgs); 0817 launch->config().writeEntry("Break on Start", true); 0818 core->runControllerInternal()->setDefaultLaunch(launch); 0819 0820 core->runControllerInternal()->execute(debugStr, launch); 0821 } else { 0822 openFiles(initialFiles); 0823 0824 for(const auto& urlinfo: qAsConst(initialDirectories)) 0825 core->projectController()->openProjectForUrl(urlinfo.url); 0826 } 0827 0828 #if KDEVELOP_SINGLE_APP 0829 // Set up remote arguments. 0830 QObject::connect(&app, &SharedTools::QtSingleApplication::messageReceived, 0831 &app, &KDevelopApplication::remoteArguments); 0832 0833 QObject::connect(&app, &SharedTools::QtSingleApplication::fileOpenRequest, 0834 &app, &KDevelopApplication::fileOpenRequested); 0835 #endif 0836 0837 0838 qCDebug(APP) << "Done startup" << "- took:" << timer.elapsed() << "ms"; 0839 timer.invalidate(); 0840 0841 return app.exec(); 0842 } 0843 0844 #include "main.moc"