1 /*
2 SPDX-FileCopyrightText: 2007 Alexander Dymo <adymo@kdevelop.org>
3 SPDX-FileCopyrightText: 2007 Kris Wong <kris.p.wong@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 #include "core.h"
9 #include "core_p.h"
10
11 #include <QApplication>
12
13 #include <KLocalizedString>
14
15 #include <language/backgroundparser/backgroundparser.h>
16 #include <language/duchain/duchain.h>
17
18 #include "mainwindow.h"
19 #include "sessioncontroller.h"
20 #include "uicontroller.h"
21 #include "plugincontroller.h"
22 #include "projectcontroller.h"
23 #include "partcontroller.h"
24 #include "languagecontroller.h"
25 #include "documentcontroller.h"
26 #include "runcontroller.h"
27 #include "session.h"
28 #include "documentationcontroller.h"
29 #include "sourceformattercontroller.h"
30 #include "progresswidget/progressmanager.h"
31 #include "selectioncontroller.h"
32 #include "debugcontroller.h"
33 #include "kdevplatform_version.h"
34 #include "workingsetcontroller.h"
35 #include "testcontroller.h"
36 #include "runtimecontroller.h"
37 #include "debug.h"
38
39 #include <csignal>
40
41 namespace {
shutdownGracefully(int sig)42 void shutdownGracefully(int sig)
43 {
44 static volatile std::sig_atomic_t handlingSignal = 0;
45
46 if ( !handlingSignal ) {
47 handlingSignal = 1;
48 qCDebug(SHELL) << "signal " << sig << " received, shutting down gracefully";
49 QCoreApplication* app = QCoreApplication::instance();
50 if (auto* guiApp = qobject_cast<QApplication*>(app)) {
51 guiApp->closeAllWindows();
52 }
53 app->quit();
54 return;
55 }
56
57 // re-raise signal with default handler and trigger program termination
58 std::signal(sig, SIG_DFL);
59 std::raise(sig);
60 }
61
installSignalHandler()62 void installSignalHandler()
63 {
64 #ifdef SIGHUP
65 std::signal(SIGHUP, shutdownGracefully);
66 #endif
67 #ifdef SIGINT
68 std::signal(SIGINT, shutdownGracefully);
69 #endif
70 #ifdef SIGTERM
71 std::signal(SIGTERM, shutdownGracefully);
72 #endif
73 }
74 }
75
76 namespace KDevelop {
77
78 Core *Core::m_self = nullptr;
79
CorePrivate(Core * core)80 CorePrivate::CorePrivate(Core *core)
81 : m_core(core)
82 , m_cleanedUp(false)
83 , m_shuttingDown(false)
84 {
85 }
86
initialize(Core::Setup mode,const QString & session)87 bool CorePrivate::initialize(Core::Setup mode, const QString& session )
88 {
89 m_mode=mode;
90
91 qCDebug(SHELL) << "Creating controllers";
92
93 if( !sessionController )
94 {
95 sessionController = new SessionController(m_core);
96 }
97 if( !workingSetController && !(mode & Core::NoUi) )
98 {
99 workingSetController = new WorkingSetController();
100 }
101 qCDebug(SHELL) << "Creating ui controller";
102 if( !uiController )
103 {
104 uiController = new UiController(m_core);
105 }
106 qCDebug(SHELL) << "Creating plugin controller";
107
108 if( !pluginController )
109 {
110 pluginController = new PluginController(m_core);
111 const auto pluginInfos = pluginController->allPluginInfos();
112 if (pluginInfos.isEmpty()) {
113 QMessageBox::critical(nullptr,
114 i18nc("@title:window", "No Plugins Found"),
115 i18n("<p>Could not find any plugins during startup.<br/>"
116 "Please make sure QT_PLUGIN_PATH is set correctly.</p>"
117 "Refer to <a href=\"https://community.kde.org/Guidelines_and_HOWTOs/Build_from_source#Set_up_the_runtime_environment\">this article</a> for more information."),
118 QMessageBox::Abort, QMessageBox::Abort);
119 qCWarning(SHELL) << "Could not find any plugins, aborting";
120 return false;
121 }
122 }
123 if( !partController && !(mode & Core::NoUi))
124 {
125 partController = new PartController(m_core, uiController->defaultMainWindow());
126 }
127
128 if( !projectController )
129 {
130 projectController = new ProjectController(m_core);
131 }
132
133 if( !documentController )
134 {
135 documentController = new DocumentController(m_core);
136 }
137
138 if( !languageController )
139 {
140 // Must be initialized after documentController, because the background parser depends
141 // on the document controller.
142 languageController = new LanguageController(m_core);
143 }
144
145 if( !runController )
146 {
147 runController = new RunController(m_core);
148 }
149
150 if( !sourceFormatterController )
151 {
152 sourceFormatterController = new SourceFormatterController(m_core);
153 }
154
155 if ( !progressController)
156 {
157 progressController = ProgressManager::instance();
158 }
159
160 if( !selectionController )
161 {
162 selectionController = new SelectionController(m_core);
163 }
164
165 if( !documentationController && !(mode & Core::NoUi) )
166 {
167 documentationController = new DocumentationController(m_core);
168 }
169
170 if( !runtimeController )
171 {
172 runtimeController = new RuntimeController(m_core);
173 }
174
175 if( !debugController )
176 {
177 debugController = new DebugController(m_core);
178 }
179
180 if( !testController )
181 {
182 testController = new TestController(m_core);
183 }
184
185 qCDebug(SHELL) << "Done creating controllers";
186
187 qCDebug(SHELL) << "Initializing controllers";
188
189 sessionController->initialize( session );
190 if( !sessionController->activeSessionLock() ) {
191 return false;
192 }
193
194 // TODO: Is this early enough, or should we put the loading of the session into
195 // the controller construct
196 DUChain::initialize();
197
198 if (!(mode & Core::NoUi)) {
199 uiController->initialize();
200 }
201 languageController->initialize();
202 languageController->backgroundParser()->suspend();
203 // eventually resume the background parser once the project controller
204 // has been initialized. At that point we know whether there are projects loading
205 // which the background parser is handling internally to defer parse jobs
206 QObject::connect(projectController.data(), &ProjectController::initialized,
207 m_core, [this]() {
208 languageController->backgroundParser()->resume();
209 });
210
211 if (partController) {
212 partController->initialize();
213 }
214 projectController->initialize();
215 documentController->initialize();
216
217 /* This is somewhat messy. We want to load the areas before
218 loading the plugins, so that when each plugin is loaded we
219 know if an area wants some of the tool view from that plugin.
220 OTOH, loading of areas creates documents, and some documents
221 might require that a plugin is already loaded.
222 Probably, the best approach would be to plugins to just add
223 tool views to a list of available tool view, and then grab
224 those tool views when loading an area. */
225
226 qCDebug(SHELL) << "Initializing plugin controller (loading session plugins)";
227 pluginController->initialize();
228
229 /* To make breakpoints show up in the UI, we need to make sure
230 DebugController is initialized and has loaded BreakpointModel
231 before UI is made visible. */
232 debugController->initialize();
233
234 qCDebug(SHELL) << "Initializing working set controller";
235 if(!(mode & Core::NoUi))
236 {
237 workingSetController->initialize();
238 /* Need to do this after everything else is loaded. It's too
239 hard to restore position of views, and toolbars, and whatever
240 that are not created yet. */
241 uiController->loadAllAreas(KSharedConfig::openConfig());
242 uiController->defaultMainWindow()->show();
243 }
244
245 qCDebug(SHELL) << "Initializing remaining controllers";
246 runController->initialize();
247 sourceFormatterController->initialize();
248 selectionController->initialize();
249 if (documentationController) {
250 documentationController->initialize();
251 }
252 testController->initialize();
253 runtimeController->initialize();
254
255 installSignalHandler();
256
257 qCDebug(SHELL) << "Done initializing controllers";
258
259 return true;
260 }
~CorePrivate()261 CorePrivate::~CorePrivate()
262 {
263 delete selectionController.data();
264 delete projectController.data();
265 delete languageController.data();
266 delete pluginController.data();
267 delete uiController.data();
268 delete partController.data();
269 delete documentController.data();
270 delete runController.data();
271 delete sessionController.data();
272 delete sourceFormatterController.data();
273 delete documentationController.data();
274 delete debugController.data();
275 delete workingSetController.data();
276 delete testController.data();
277 delete runtimeController.data();
278 }
279
initialize(Setup mode,const QString & session)280 bool Core::initialize(Setup mode, const QString& session)
281 {
282 if (m_self)
283 return true;
284
285 m_self = new Core();
286 bool ret = m_self->d->initialize(mode, session);
287
288 if(ret)
289 emit m_self->initializationCompleted();
290
291 return ret;
292 }
293
self()294 Core *KDevelop::Core::self()
295 {
296 return m_self;
297 }
298
Core(QObject * parent)299 Core::Core(QObject *parent)
300 : ICore(parent)
301 {
302 d = new CorePrivate(this);
303
304 connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &Core::shutdown);
305 }
306
Core(CorePrivate * dd,QObject * parent)307 Core::Core(CorePrivate* dd, QObject* parent)
308 : ICore(parent), d(dd)
309 {
310 connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &Core::shutdown);
311 }
312
~Core()313 Core::~Core()
314 {
315 qCDebug(SHELL);
316
317 //Cleanup already called before mass destruction of GUI
318 delete d;
319 m_self = nullptr;
320 }
321
setupFlags() const322 Core::Setup Core::setupFlags() const
323 {
324 return d->m_mode;
325 }
326
shutdown()327 void Core::shutdown()
328 {
329 qCDebug(SHELL);
330
331 if (!d->m_shuttingDown) {
332 cleanup();
333 deleteLater();
334 }
335
336 qCDebug(SHELL) << "Shutdown done";
337 }
338
shuttingDown() const339 bool Core::shuttingDown() const
340 {
341 return d->m_shuttingDown;
342 }
343
cleanup()344 void Core::cleanup()
345 {
346 qCDebug(SHELL);
347
348 d->m_shuttingDown = true;
349 emit aboutToShutdown();
350
351 if (!d->m_cleanedUp) {
352 // first of all: request stop of all background parser jobs
353 d->languageController->backgroundParser()->abortAllJobs();
354 d->languageController->backgroundParser()->suspend();
355
356 d->debugController->cleanup();
357 d->selectionController->cleanup();
358
359 if (!(d->m_mode & Core::NoUi)) {
360 // Save the layout of the ui here, so run it first
361 d->uiController->cleanup();
362 }
363
364 if (d->workingSetController)
365 d->workingSetController->cleanup();
366
367 /* Must be called before projectController->cleanup(). */
368 // Closes all documents (discards, as already saved if the user wished earlier)
369 d->documentController->cleanup();
370 d->runController->cleanup();
371 if (d->partController) {
372 d->partController->cleanup();
373 }
374 d->projectController->cleanup();
375 d->sourceFormatterController->cleanup();
376
377 // before unloading language plugins, we need to make sure all parse jobs are done
378 d->languageController->backgroundParser()->waitForIdle();
379
380 DUChain::self()->shutdown();
381
382 // Only unload plugins after the DUChain shutdown to prevent issues with non-loaded factories for types
383 // See: https://bugs.kde.org/show_bug.cgi?id=379669
384 d->pluginController->cleanup();
385
386 d->sessionController->cleanup();
387
388 d->testController->cleanup();
389
390 //Disable the functionality of the language controller
391 d->languageController->cleanup();
392 }
393
394 d->m_cleanedUp = true;
395 emit shutdownCompleted();
396 }
397
uiController()398 IUiController *Core::uiController()
399 {
400 return d->uiController.data();
401 }
402
activeSession()403 ISession* Core::activeSession()
404 {
405 return sessionController()->activeSession();
406 }
407
activeSessionLock()408 ISessionLock::Ptr Core::activeSessionLock()
409 {
410 return sessionController()->activeSessionLock();
411 }
412
sessionController()413 SessionController *Core::sessionController()
414 {
415 return d->sessionController.data();
416 }
417
uiControllerInternal()418 UiController *Core::uiControllerInternal()
419 {
420 return d->uiController.data();
421 }
422
pluginController()423 IPluginController *Core::pluginController()
424 {
425 return d->pluginController.data();
426 }
427
pluginControllerInternal()428 PluginController *Core::pluginControllerInternal()
429 {
430 return d->pluginController.data();
431 }
432
projectController()433 IProjectController *Core::projectController()
434 {
435 return d->projectController.data();
436 }
437
projectControllerInternal()438 ProjectController *Core::projectControllerInternal()
439 {
440 return d->projectController.data();
441 }
442
partController()443 IPartController *Core::partController()
444 {
445 return d->partController.data();
446 }
447
partControllerInternal()448 PartController *Core::partControllerInternal()
449 {
450 return d->partController.data();
451 }
452
languageController()453 ILanguageController *Core::languageController()
454 {
455 return d->languageController.data();
456 }
457
languageControllerInternal()458 LanguageController *Core::languageControllerInternal()
459 {
460 return d->languageController.data();
461 }
462
documentController()463 IDocumentController *Core::documentController()
464 {
465 return d->documentController.data();
466 }
467
documentControllerInternal()468 DocumentController *Core::documentControllerInternal()
469 {
470 return d->documentController.data();
471 }
472
runController()473 IRunController *Core::runController()
474 {
475 return d->runController.data();
476 }
477
runControllerInternal()478 RunController *Core::runControllerInternal()
479 {
480 return d->runController.data();
481 }
482
sourceFormatterController()483 ISourceFormatterController* Core::sourceFormatterController()
484 {
485 return d->sourceFormatterController.data();
486 }
487
sourceFormatterControllerInternal()488 SourceFormatterController* Core::sourceFormatterControllerInternal()
489 {
490 return d->sourceFormatterController.data();
491 }
492
493
progressController()494 ProgressManager *Core::progressController()
495 {
496 return d->progressController.data();
497 }
498
selectionController()499 ISelectionController* Core::selectionController()
500 {
501 return d->selectionController.data();
502 }
503
documentationController()504 IDocumentationController* Core::documentationController()
505 {
506 return d->documentationController.data();
507 }
508
documentationControllerInternal()509 DocumentationController* Core::documentationControllerInternal()
510 {
511 return d->documentationController.data();
512 }
513
runtimeController()514 IRuntimeController* Core::runtimeController()
515 {
516 return d->runtimeController.data();
517 }
518
runtimeControllerInternal()519 RuntimeController* Core::runtimeControllerInternal()
520 {
521 return d->runtimeController.data();
522 }
523
debugController()524 IDebugController* Core::debugController()
525 {
526 return d->debugController.data();
527 }
528
debugControllerInternal()529 DebugController* Core::debugControllerInternal()
530 {
531 return d->debugController.data();
532 }
533
testController()534 ITestController* Core::testController()
535 {
536 return d->testController.data();
537 }
538
testControllerInternal()539 TestController* Core::testControllerInternal()
540 {
541 return d->testController.data();
542 }
543
workingSetControllerInternal()544 WorkingSetController* Core::workingSetControllerInternal()
545 {
546 return d->workingSetController.data();
547 }
548
version()549 QString Core::version()
550 {
551 return QStringLiteral(KDEVPLATFORM_VERSION_STRING);
552 }
553
554 }
555
556