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