1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3  * http://www.gnu.org/licenses/gpl-3.0.html
4  *
5  * $Revision: 11845 $
6  * $Id: compilergcc.cpp 11845 2019-09-08 22:37:48Z fuscated $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/plugins/compilergcc/compilergcc.cpp $
8  */
9 
10 #include <sdk.h>
11 
12 #include <wx/frame.h> // GetMenuBar
13 #include <wx/gauge.h> // Needs to be before compilergcc.h if NOPCH on wxMSW
14 #include <wx/listctrl.h>
15 #include <wx/xrc/xmlres.h>
16 #include <wx/sizer.h>
17 #include <wx/button.h>
18 #include <wx/stattext.h>
19 #include <wx/statline.h>
20 #include <wx/ffile.h>
21 #include <wx/utils.h>
22 #include <wx/uri.h>
23 #include <wx/xml/xml.h>
24 
25 #ifndef CB_PRECOMP
26     #include <wx/app.h>
27     #include <wx/toolbar.h>
28 #endif
29 
30 #include <prep.h>
31 #include <manager.h>
32 #include <sdk_events.h>
33 #include <pipedprocess.h>
34 #include <configmanager.h>
35 #include <compilercommandgenerator.h>
36 #include <debuggermanager.h>
37 #include <incremental_select_helper.h>
38 #include <logmanager.h>
39 #include <macrosmanager.h>
40 #include <projectmanager.h>
41 #include <editormanager.h>
42 #include <scriptingmanager.h>
43 #include <configurationpanel.h>
44 #include <pluginmanager.h>
45 #include <cbeditor.h>
46 #include <annoyingdialog.h>
47 #include <filefilters.h>
48 #include <infowindow.h>
49 
50 #include "compilergcc.h"
51 #include "compileroptionsdlg.h"
52 #include "directcommands.h"
53 #include "globals.h"
54 #include "cbart_provider.h"
55 #include "cbworkspace.h"
56 #include "cbstyledtextctrl.h"
57 
58 
59 #include "compilerMINGW.h"
60 #include "compilerGNUARM.h"
61 #include "compilerMSVC.h"
62 #include "compilerMSVC8.h"
63 #include "compilerMSVC10.h"
64 #include "compilerOW.h"
65 #include "compilerGNUARM.h"
66 #include "compilerCYGWIN.h"
67 #include "compilerLCC.h"
68 #include "compilerKeilC51.h"
69 #include "compilerIAR.h"
70 #include "compilerICC.h"
71 #include "compilerGDC.h"
72 #include "compilerGNUFortran.h"
73 #include "compilerG95.h"
74 #include "compilerXML.h"
75 
76 #include <scripting/bindings/sc_base_types.h>
77 
78 namespace ScriptBindings
79 {
80     static int gBuildLogId = -1;
81 
82     // global funcs
gBuildLog(const wxString & msg)83     void gBuildLog(const wxString& msg)
84     {
85         Manager::Get()->GetLogManager()->Log(msg, gBuildLogId);
86     }
87 }
88 
89 const int idBuildLog = wxNewId();
90 
91 class BuildLogger : public TextCtrlLogger
92 {
93     wxPanel* panel;
94     wxBoxSizer* sizer;
95 public:
96     wxGauge* progress;
97 
BuildLogger()98     BuildLogger() : TextCtrlLogger(true), panel(0), sizer(0), progress(0) {}
99 
UpdateSettings()100     void UpdateSettings() override
101     {
102         TextCtrlLogger::UpdateSettings();
103 
104         style[caption].SetAlignment(wxTEXT_ALIGNMENT_DEFAULT);
105         style[caption].SetFont(style[error].GetFont());
106         style[error].SetFont(style[info].GetFont());
107     }
108 
CreateControl(wxWindow * parent)109     wxWindow* CreateControl(wxWindow* parent) override
110     {
111         panel = new wxPanel(parent);
112 
113         TextCtrlLogger::CreateControl(panel);
114         control->SetId(idBuildLog);
115 
116         sizer = new wxBoxSizer(wxVERTICAL);
117         sizer->Add(control, 1, wxEXPAND, 0);
118         panel->SetSizer(sizer);
119 
120         return panel;
121     }
122 
AddBuildProgressBar()123     void AddBuildProgressBar()
124     {
125         if (!progress)
126         {
127             progress = new wxGauge(panel, -1, 0, wxDefaultPosition, wxSize(-1, 12));
128             sizer->Add(progress, 0, wxEXPAND);
129             sizer->Layout();
130         }
131     }
132 
RemoveBuildProgressBar()133     void RemoveBuildProgressBar()
134     {
135         if (progress)
136         {
137             sizer->Detach(progress);
138             progress->Destroy();
139             progress = 0;
140             sizer->Layout();
141         }
142     }
143 
OpenLink(long urlStart,long urlEnd)144     void OpenLink(long urlStart, long urlEnd)
145     {
146         if (!control)
147             return;
148         wxString url = control->GetRange(urlStart, urlEnd);
149         if (platform::windows && url.StartsWith(_T("file://")))
150             url.Remove(0, 7);
151         cbMimePlugin* p = Manager::Get()->GetPluginManager()->GetMIMEHandlerForFile(url);
152         if (p)
153             p->OpenFile(url);
154         else
155             wxLaunchDefaultBrowser(url);
156     }
157 };
158 
159 namespace
160 {
161     PluginRegistrant<CompilerGCC> reg(_T("Compiler"));
162 
163     static const wxString strCONSOLE_RUNNER(platform::windows ? _T("cb_console_runner.exe") : _T("cb_console_runner"));
164     static const wxString strSLASH(_T("/"));
165     static const wxString strSPACE(_T(" "));
166     static const wxString strQUOTE(platform::windows ? _T("\"") : _T("'"));
167 }
168 
169 // menu IDS
170 // just because we don't know other plugins' used identifiers,
171 // we use wxNewId() to generate a guaranteed unique ID ;), instead of enum
172 // (don't forget that, especially in a plugin)
173 int idTimerPollCompiler                            = XRCID("idTimerPollCompiler");
174 int idMenuCompile                                  = XRCID("idCompilerMenuCompile");
175 int idMenuCompileTarget                            = wxNewId();
176 int idMenuCompileFromProjectManager                = wxNewId();
177 int idMenuProjectCompilerOptions                   = wxNewId();
178 int idMenuProjectCompilerOptionsFromProjectManager = wxNewId();
179 int idMenuTargetCompilerOptions                    = wxNewId();
180 int idMenuTargetCompilerOptionsSub                 = wxNewId();
181 int idMenuCompileFile                              = XRCID("idCompilerMenuCompileFile");
182 int idMenuCompileFileFromProjectManager            = wxNewId();
183 int idMenuCleanFileFromProjectManager              = wxNewId();
184 int idMenuRebuild                                  = XRCID("idCompilerMenuRebuild");
185 int idMenuRebuildTarget                            = wxNewId();
186 int idMenuRebuildFromProjectManager                = wxNewId();
187 int idMenuClean                                    = XRCID("idCompilerMenuClean");
188 int idMenuBuildWorkspace                           = XRCID("idCompilerMenuBuildWorkspace");
189 int idMenuRebuildWorkspace                         = XRCID("idCompilerMenuRebuildWorkspace");
190 int idMenuCleanWorkspace                           = XRCID("idCompilerMenuCleanWorkspace");
191 int idMenuCleanTarget                              = wxNewId();
192 int idMenuCleanFromProjectManager                  = wxNewId();
193 int idMenuCompileAndRun                            = XRCID("idCompilerMenuCompileAndRun");
194 int idMenuRun                                      = XRCID("idCompilerMenuRun");
195 int idMenuKillProcess                              = XRCID("idCompilerMenuKillProcess");
196 int idMenuSelectTarget                             = XRCID("idCompilerMenuSelectTarget");
197 
198 // Limit the number of menu items to try to make them all visible on the screen.
199 // Scrolling menus is not the best user experience.
200 const int maxTargetInMenus = 40;
201 int idMenuSelectTargetOther[maxTargetInMenus]; // initialized in ctor
202 int idMenuSelectTargetDialog                       = XRCID("idMenuSelectTargetDialog");
203 int idMenuSelectTargetHasMore                      = wxNewId();
204 
205 int idMenuNextError                                = XRCID("idCompilerMenuNextError");
206 int idMenuPreviousError                            = XRCID("idCompilerMenuPreviousError");
207 int idMenuClearErrors                              = XRCID("idCompilerMenuClearErrors");
208 int idMenuSettings                                 = XRCID("idCompilerMenuSettings");
209 
210 int idToolTarget                                   = XRCID("idToolTarget");
211 
212 int idGCCProcess = wxNewId();
213 
BEGIN_EVENT_TABLE(CompilerGCC,cbCompilerPlugin)214 BEGIN_EVENT_TABLE(CompilerGCC, cbCompilerPlugin)
215     EVT_UPDATE_UI(idMenuCompile,                       CompilerGCC::OnUpdateUI)
216     EVT_UPDATE_UI(idMenuCompileTarget,                 CompilerGCC::OnUpdateUI)
217     EVT_UPDATE_UI(idMenuCompileFromProjectManager,     CompilerGCC::OnUpdateUI)
218     EVT_UPDATE_UI(idMenuProjectCompilerOptions,        CompilerGCC::OnUpdateUI)
219     EVT_UPDATE_UI(idMenuTargetCompilerOptions,         CompilerGCC::OnUpdateUI)
220     EVT_UPDATE_UI(idMenuTargetCompilerOptionsSub,      CompilerGCC::OnUpdateUI)
221     EVT_UPDATE_UI(idMenuCompileFile,                   CompilerGCC::OnUpdateUI)
222     EVT_UPDATE_UI(idMenuCompileFileFromProjectManager, CompilerGCC::OnUpdateUI)
223     EVT_UPDATE_UI(idMenuCleanFileFromProjectManager,   CompilerGCC::OnUpdateUI)
224     EVT_UPDATE_UI(idMenuRebuild,                       CompilerGCC::OnUpdateUI)
225     EVT_UPDATE_UI(idMenuRebuildTarget,                 CompilerGCC::OnUpdateUI)
226     EVT_UPDATE_UI(idMenuRebuildFromProjectManager,     CompilerGCC::OnUpdateUI)
227     EVT_UPDATE_UI(idMenuBuildWorkspace,                CompilerGCC::OnUpdateUI)
228     EVT_UPDATE_UI(idMenuRebuildWorkspace,              CompilerGCC::OnUpdateUI)
229     EVT_UPDATE_UI(idMenuClean,                         CompilerGCC::OnUpdateUI)
230     EVT_UPDATE_UI(idMenuCleanWorkspace,                CompilerGCC::OnUpdateUI)
231     EVT_UPDATE_UI(idMenuCleanTarget,                   CompilerGCC::OnUpdateUI)
232     EVT_UPDATE_UI(idMenuCleanFromProjectManager,       CompilerGCC::OnUpdateUI)
233     EVT_UPDATE_UI(idMenuCompileAndRun,                 CompilerGCC::OnUpdateUI)
234     EVT_UPDATE_UI(idMenuRun,                           CompilerGCC::OnUpdateUI)
235     EVT_UPDATE_UI(idMenuKillProcess,                   CompilerGCC::OnUpdateUI)
236     EVT_UPDATE_UI(idMenuSelectTarget,                  CompilerGCC::OnUpdateUI)
237     EVT_UPDATE_UI(idMenuNextError,                     CompilerGCC::OnUpdateUI)
238     EVT_UPDATE_UI(idMenuPreviousError,                 CompilerGCC::OnUpdateUI)
239     EVT_UPDATE_UI(idMenuClearErrors,                   CompilerGCC::OnUpdateUI)
240     EVT_UPDATE_UI(idMenuSettings,                      CompilerGCC::OnUpdateUI)
241     EVT_UPDATE_UI(idToolTarget,                        CompilerGCC::OnUpdateUI)
242     EVT_UPDATE_UI(idMenuSelectTargetDialog,            CompilerGCC::OnUpdateUI)
243 
244     EVT_IDLE(                                       CompilerGCC::OnIdle)
245     EVT_TIMER(idTimerPollCompiler,                  CompilerGCC::OnTimer)
246 
247     EVT_MENU(idMenuRun,                             CompilerGCC::Dispatcher)
248     EVT_MENU(idMenuCompileAndRun,                   CompilerGCC::Dispatcher)
249     EVT_MENU(idMenuCompile,                         CompilerGCC::Dispatcher)
250     EVT_MENU(idMenuCompileFromProjectManager,       CompilerGCC::Dispatcher)
251     EVT_MENU(idMenuCompileFile,                     CompilerGCC::Dispatcher)
252     EVT_MENU(idMenuCompileFileFromProjectManager,   CompilerGCC::Dispatcher)
253     EVT_MENU(idMenuCleanFileFromProjectManager,     CompilerGCC::Dispatcher)
254     EVT_MENU(idMenuRebuild,                         CompilerGCC::Dispatcher)
255     EVT_MENU(idMenuRebuildFromProjectManager,       CompilerGCC::Dispatcher)
256     EVT_MENU(idMenuBuildWorkspace,                  CompilerGCC::Dispatcher)
257     EVT_MENU(idMenuRebuildWorkspace,                CompilerGCC::Dispatcher)
258     EVT_MENU(idMenuProjectCompilerOptions,          CompilerGCC::Dispatcher)
259     EVT_MENU(idMenuProjectCompilerOptionsFromProjectManager, CompilerGCC::Dispatcher)
260     EVT_MENU(idMenuTargetCompilerOptions,           CompilerGCC::Dispatcher)
261     EVT_MENU(idMenuClean,                           CompilerGCC::Dispatcher)
262     EVT_MENU(idMenuCleanWorkspace,                  CompilerGCC::Dispatcher)
263     EVT_MENU(idMenuCleanFromProjectManager,         CompilerGCC::Dispatcher)
264     EVT_MENU(idMenuKillProcess,                     CompilerGCC::Dispatcher)
265     EVT_MENU(idMenuNextError,                       CompilerGCC::Dispatcher)
266     EVT_MENU(idMenuPreviousError,                   CompilerGCC::Dispatcher)
267     EVT_MENU(idMenuClearErrors,                     CompilerGCC::Dispatcher)
268     EVT_MENU(idMenuSettings,                        CompilerGCC::Dispatcher)
269 
270     EVT_TEXT_URL(idBuildLog,                        CompilerGCC::TextURL)
271 
272     EVT_CHOICE(idToolTarget,                        CompilerGCC::OnSelectTarget)
273     EVT_MENU(idMenuSelectTargetDialog,              CompilerGCC::OnSelectTarget)
274 
275     EVT_PIPEDPROCESS_STDOUT(idGCCProcess, CompilerGCC::OnGCCOutput)
276     EVT_PIPEDPROCESS_STDERR(idGCCProcess, CompilerGCC::OnGCCError)
277     EVT_PIPEDPROCESS_TERMINATED(idGCCProcess, CompilerGCC::OnGCCTerminated)
278 END_EVENT_TABLE()
279 
280 CompilerGCC::CompilerGCC() :
281     m_RealTargetsStartIndex(0),
282     m_RealTargetIndex(0),
283     m_PageIndex(-1),
284     m_ListPageIndex(-1),
285     m_Menu(0L),
286     m_TargetMenu(0L),
287     m_TargetIndex(-1),
288     m_pErrorsMenu(0L),
289     m_pProject(0L),
290     m_pTbar(0L),
291     m_pLog(0L),
292     m_pListLog(0L),
293     m_pToolTarget(0L),
294     m_RunAfterCompile(false),
295     m_LastExitCode(0),
296     m_NotifiedMaxErrors(false),
297     m_pBuildingProject(0),
298     m_BuildJob(bjIdle),
299     m_NextBuildState(bsNone),
300     m_pLastBuildingProject(0),
301     m_pLastBuildingTarget(0),
302     m_Clean(false),
303     m_Build(false),
304     m_LastBuildStep(true),
305     m_RunTargetPostBuild(false),
306     m_RunProjectPostBuild(false),
307     m_IsWorkspaceOperation(false),
308     m_LogBuildProgressPercentage(false),
309     m_pArtProvider(nullptr)
310 {
311     if (!Manager::LoadResource(_T("compiler.zip")))
312         NotifyMissingFile(_T("compiler.zip"));
313 }
314 
~CompilerGCC()315 CompilerGCC::~CompilerGCC()
316 {
317 }
318 
OnAttach()319 void CompilerGCC::OnAttach()
320 {
321     // reset all vars
322     m_RealTargetsStartIndex = 0;
323     m_RealTargetIndex = 0;
324     m_PageIndex = -1;
325     m_ListPageIndex = -1;
326     m_Menu = 0L;
327     m_TargetMenu = 0L;
328     m_TargetIndex = -1;
329     m_pErrorsMenu = 0L;
330     m_pProject = 0L;
331     m_pTbar = 0L;
332     m_pLog = 0L;
333     m_pListLog = 0L;
334     m_pToolTarget = 0L;
335     m_RunAfterCompile = false;
336     m_LastExitCode = 0;
337     m_NotifiedMaxErrors = false;
338     m_pBuildingProject = 0;
339     m_BuildJob = bjIdle;
340     m_NextBuildState = bsNone;
341     m_pLastBuildingProject = 0;
342     m_pLastBuildingTarget = 0;
343     m_RunTargetPostBuild = false;
344     m_RunProjectPostBuild = false;
345     m_Clean = false;
346     m_Build = false;
347     m_LastBuildStep = true;
348     m_IsWorkspaceOperation = false;
349 
350     m_timerIdleWakeUp.SetOwner(this, idTimerPollCompiler);
351 
352     for (int i = 0; i < maxTargetInMenus; ++i)
353         idMenuSelectTargetOther[i] = wxNewId();
354 
355     DoRegisterCompilers();
356 
357     AllocProcesses();
358 
359     LogManager* msgMan = Manager::Get()->GetLogManager();
360 
361     {
362         const wxString prefix = ConfigManager::GetDataFolder() + wxT("/compiler.zip#zip:/images");
363         m_pArtProvider = new cbArtProvider(prefix);
364 
365         m_pArtProvider->AddMapping(wxT("compiler/compile"), wxT("compile.png"));
366         m_pArtProvider->AddMapping(wxT("compiler/run"), wxT("run.png"));
367         m_pArtProvider->AddMapping(wxT("compiler/compile_run"), wxT("compilerun.png"));
368         m_pArtProvider->AddMapping(wxT("compiler/rebuild"), wxT("rebuild.png"));
369         m_pArtProvider->AddMapping(wxT("compiler/stop"), wxT("stop.png"));
370 
371         wxArtProvider::Push(m_pArtProvider);
372     }
373 
374     // create compiler's log
375     m_pLog = new BuildLogger();
376     m_PageIndex = msgMan->SetLog(m_pLog);
377     msgMan->Slot(m_PageIndex).title = _("Build log");
378 //    msgMan->SetBatchBuildLog(m_PageIndex);
379     // set log image
380     const int uiSize = Manager::Get()->GetImageSize(Manager::UIComponent::InfoPaneNotebooks);
381     const int uiScaleFactor = Manager::Get()->GetUIScaleFactor(Manager::UIComponent::InfoPaneNotebooks);
382     const wxString prefix = ConfigManager::GetDataFolder()
383                           + wxString::Format(_T("/resources.zip#zip:/images/infopane/%dx%d/"),
384                                              uiSize, uiSize);
385     wxBitmap* bmp = new wxBitmap(cbLoadBitmapScaled(prefix + _T("misc.png"), wxBITMAP_TYPE_PNG,
386                                                     uiScaleFactor));
387     msgMan->Slot(m_PageIndex).icon = bmp;
388 
389     // create warnings/errors log
390     wxArrayString titles;
391     wxArrayInt widths;
392     titles.Add(_("File"));
393     titles.Add(_("Line"));
394     titles.Add(_("Message"));
395     widths.Add(128);
396     widths.Add(48);
397     widths.Add(640);
398 
399     m_pListLog = new CompilerMessages(titles, widths);
400     m_pListLog->SetCompilerErrors(&m_Errors);
401     m_ListPageIndex = msgMan->SetLog(m_pListLog);
402     msgMan->Slot(m_ListPageIndex).title = _("Build messages");
403     // set log image
404     bmp = new wxBitmap(cbLoadBitmapScaled(prefix + _T("flag.png"), wxBITMAP_TYPE_PNG,
405                                           uiScaleFactor));
406     msgMan->Slot(m_ListPageIndex).icon = bmp;
407 
408     CodeBlocksLogEvent evtAdd1(cbEVT_ADD_LOG_WINDOW, m_pLog, msgMan->Slot(m_PageIndex).title, msgMan->Slot(m_PageIndex).icon);
409     Manager::Get()->ProcessEvent(evtAdd1);
410     if (!Manager::IsBatchBuild())
411     {
412         CodeBlocksLogEvent evtAdd2(cbEVT_ADD_LOG_WINDOW, m_pListLog, msgMan->Slot(m_ListPageIndex).title, msgMan->Slot(m_ListPageIndex).icon);
413         Manager::Get()->ProcessEvent(evtAdd2);
414     }
415 
416     m_LogBuildProgressPercentage = Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/build_progress/percentage"), false);
417     bool hasBuildProg = Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/build_progress/bar"), false);
418     if (hasBuildProg)
419         m_pLog->AddBuildProgressBar();
420 
421     // set default compiler for new projects
422     CompilerFactory::SetDefaultCompiler(Manager::Get()->GetConfigManager(_T("compiler"))->Read(_T("/default_compiler"), _T("gcc")));
423     LoadOptions();
424 
425     // register compiler's script functions
426     // make sure the VM is initialized
427     Manager::Get()->GetScriptingManager();
428     if (SquirrelVM::GetVMPtr())
429     {
430         ScriptBindings::gBuildLogId = m_PageIndex;
431         SqPlus::RegisterGlobal(ScriptBindings::gBuildLog, "LogBuild");
432     }
433     else
434         ScriptBindings::gBuildLogId = -1;
435 
436     // register event sink
437     Manager::Get()->RegisterEventSink(cbEVT_PROJECT_ACTIVATE,         new cbEventFunctor<CompilerGCC, CodeBlocksEvent>(this, &CompilerGCC::OnProjectActivated));
438     Manager::Get()->RegisterEventSink(cbEVT_PROJECT_OPEN,             new cbEventFunctor<CompilerGCC, CodeBlocksEvent>(this, &CompilerGCC::OnProjectLoaded));
439     Manager::Get()->RegisterEventSink(cbEVT_PROJECT_CLOSE,            new cbEventFunctor<CompilerGCC, CodeBlocksEvent>(this, &CompilerGCC::OnProjectUnloaded));
440     Manager::Get()->RegisterEventSink(cbEVT_PROJECT_TARGETS_MODIFIED, new cbEventFunctor<CompilerGCC, CodeBlocksEvent>(this, &CompilerGCC::OnProjectActivated));
441     Manager::Get()->RegisterEventSink(cbEVT_WORKSPACE_CLOSING_COMPLETE, new cbEventFunctor<CompilerGCC, CodeBlocksEvent>(this, &CompilerGCC::OnWorkspaceClosed));
442 
443     Manager::Get()->RegisterEventSink(cbEVT_COMPILE_FILE_REQUEST,     new cbEventFunctor<CompilerGCC, CodeBlocksEvent>(this, &CompilerGCC::OnCompileFileRequest));
444 }
445 
OnRelease(bool appShutDown)446 void CompilerGCC::OnRelease(bool appShutDown)
447 {
448     // disable script functions
449     ScriptBindings::gBuildLogId = -1;
450 
451     SaveOptions();
452     Manager::Get()->GetConfigManager(_T("compiler"))->Write(_T("/default_compiler"), CompilerFactory::GetDefaultCompilerID());
453     LogManager *logManager = Manager::Get()->GetLogManager();
454     if (logManager)
455     {
456         // for batch builds, the log is deleted by the manager
457         if (!Manager::IsBatchBuild())
458         {
459             CodeBlocksLogEvent evt(cbEVT_REMOVE_LOG_WINDOW, m_pLog);
460             Manager::Get()->ProcessEvent(evt);
461         }
462 
463         {
464             // TODO: This is wrong. We need some automatic way for this to happen!!!
465             LogSlot &listSlot = logManager->Slot(m_ListPageIndex);
466             delete listSlot.icon;
467             listSlot.icon = nullptr;
468 
469             LogSlot &slot = logManager->Slot(m_PageIndex);
470             delete slot.icon;
471             slot.icon = nullptr;
472         }
473 
474         m_pLog = 0;
475 
476         CodeBlocksLogEvent evt(cbEVT_REMOVE_LOG_WINDOW, m_pListLog);
477         m_pListLog->DestroyControls();
478         Manager::Get()->ProcessEvent(evt);
479         m_pListLog = 0;
480     }
481 
482     // let wx handle this on shutdown ( if we return here Valgrind will be sad :'( )
483     if (!appShutDown)
484         DoClearTargetMenu();
485 
486     m_timerIdleWakeUp.Stop();
487 
488     FreeProcesses();
489 
490     CompilerFactory::UnregisterCompilers();
491 
492     wxArtProvider::Delete(m_pArtProvider);
493     m_pArtProvider = nullptr;
494 }
495 
Configure(cbProject * project,ProjectBuildTarget * target,wxWindow * parent)496 int CompilerGCC::Configure(cbProject* project, ProjectBuildTarget* target, wxWindow *parent)
497 {
498     cbConfigurationDialog dlg(parent, wxID_ANY, _("Project build options"));
499     cbConfigurationPanel* panel = new CompilerOptionsDlg(&dlg, this, project, target);
500     panel->SetParentDialog(&dlg);
501     dlg.AttachConfigurationPanel(panel);
502     PlaceWindow(&dlg);
503     if (dlg.ShowModal() == wxID_OK)
504     {
505         SaveOptions();
506         Manager::Get()->GetMacrosManager()->Reset();
507 
508         bool hasBuildProg = Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/build_progress/bar"), false);
509         if (hasBuildProg)
510             m_pLog->AddBuildProgressBar();
511         else
512             m_pLog->RemoveBuildProgressBar();
513     }
514 //    delete panel;
515     return 0;
516 }
517 
GetConfigurationPanel(wxWindow * parent)518 cbConfigurationPanel* CompilerGCC::GetConfigurationPanel(wxWindow* parent)
519 {
520     CompilerOptionsDlg* dlg = new CompilerOptionsDlg(parent, this, 0, 0);
521     return dlg;
522 }
523 
OnConfig(cb_unused wxCommandEvent & event)524 void CompilerGCC::OnConfig(cb_unused wxCommandEvent& event)
525 {
526     Configure(nullptr, nullptr, Manager::Get()->GetAppWindow());
527 }
528 
BuildMenu(wxMenuBar * menuBar)529 void CompilerGCC::BuildMenu(wxMenuBar* menuBar)
530 {
531     if (!IsAttached())
532         return;
533 
534     m_Menu = Manager::Get()->LoadMenu(_T("compiler_menu"),true);
535 
536     // target selection menu
537     wxMenuItem *tmpitem=m_Menu->FindItem(idMenuSelectTarget,NULL);
538     m_TargetMenu = tmpitem ? tmpitem->GetSubMenu() : new wxMenu(_T(""));
539     DoRecreateTargetMenu();
540     //m_Menu->Append(idMenuSelectTarget, _("Select target..."), m_TargetMenu);
541 
542     // ok, now, where do we insert?
543     // three possibilities here:
544     // a) locate "Debug" menu and insert before it
545     // b) locate "Project" menu and insert after it
546     // c) if not found (?), insert at pos 5
547     int finalPos = 5;
548     int projMenuPos = menuBar->FindMenu(_("&Debug"));
549     if (projMenuPos != wxNOT_FOUND)
550         finalPos = projMenuPos;
551     else
552     {
553         projMenuPos = menuBar->FindMenu(_("&Project"));
554         if (projMenuPos != wxNOT_FOUND)
555             finalPos = projMenuPos + 1;
556     }
557     menuBar->Insert(finalPos, m_Menu, _("&Build"));
558 
559     // now add some entries in Project menu
560     projMenuPos = menuBar->FindMenu(_("&Project"));
561     if (projMenuPos != wxNOT_FOUND)
562     {
563         wxMenu* prj = menuBar->GetMenu(projMenuPos);
564         // look if we have a "Properties" item. If yes, we 'll insert
565         // before it, else we 'll just append...
566         size_t propsPos = prj->GetMenuItemCount(); // append
567         const int idMenuProjectProperties = prj->FindItem(_("Properties..."));
568         if (idMenuProjectProperties != wxNOT_FOUND)
569             prj->FindChildItem(idMenuProjectProperties, &propsPos);
570         prj->Insert(propsPos, idMenuProjectCompilerOptions, _("Build options..."), _("Set the project's build options"));
571         prj->InsertSeparator(propsPos);
572     }
573 }
574 
BuildModuleMenu(const ModuleType type,wxMenu * menu,const FileTreeData * data)575 void CompilerGCC::BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data)
576 {
577     if (!IsAttached())
578         return;
579     // we 're only interested in project manager's menus
580     if (type != mtProjectManager || !menu)
581         return;
582 
583     if (!CheckProject())
584         return;
585 
586     if (!data || data->GetKind() == FileTreeData::ftdkUndefined)
587     {
588         // popup menu in empty space in ProjectManager
589         if (menu->GetMenuItemCount() > 0)
590             menu->AppendSeparator();
591         menu->Append(idMenuBuildWorkspace,   _("Build workspace"));
592         menu->Append(idMenuRebuildWorkspace, _("Rebuild workspace"));
593         menu->Append(idMenuCleanWorkspace,   _("Clean workspace"));
594 
595         if (IsRunning())
596         {
597             menu->Enable(idMenuBuildWorkspace, false);
598             menu->Enable(idMenuRebuildWorkspace, false);
599             menu->Enable(idMenuCleanWorkspace, false);
600         }
601     }
602     else if (data && data->GetKind() == FileTreeData::ftdkProject)
603     {
604         // popup menu on a project
605         wxMenuItem* itm = menu->FindItemByPosition(menu->GetMenuItemCount() - 1);
606         if (itm && !itm->IsSeparator())
607             menu->AppendSeparator();
608         menu->Append(idMenuCompileFromProjectManager, _("Build"));
609         menu->Append(idMenuRebuildFromProjectManager, _("Rebuild"));
610         menu->Append(idMenuCleanFromProjectManager,   _("Clean"));
611         menu->AppendSeparator();
612         menu->Append(idMenuProjectCompilerOptionsFromProjectManager, _("Build options..."));
613 
614         cbPlugin *otherRunning = Manager::Get()->GetProjectManager()->GetIsRunning();
615         if (IsRunning() || (otherRunning && otherRunning != this))
616         {
617             menu->Enable(idMenuCompileFromProjectManager, false);
618             menu->Enable(idMenuRebuildFromProjectManager, false);
619             menu->Enable(idMenuCleanFromProjectManager, false);
620             menu->Enable(idMenuProjectCompilerOptionsFromProjectManager, false);
621         }
622     }
623     else if (data && data->GetKind() == FileTreeData::ftdkFile)
624     {
625         FileType ft = FileTypeOf(data->GetProjectFile()->relativeFilename);
626         if (ft == ftSource || ft == ftHeader || ft == ftTemplateSource)
627         {
628             // popup menu on a compilable file
629             menu->AppendSeparator();
630             menu->Append(idMenuCompileFileFromProjectManager, _("Build file"));
631             menu->Append(idMenuCleanFileFromProjectManager,   _("Clean file"));
632             if (IsRunning())
633             {
634                 menu->Enable(idMenuCompileFileFromProjectManager, false);
635                 menu->Enable(idMenuCleanFileFromProjectManager,   false);
636             }
637         }
638     }
639 }
640 
BuildToolBar(wxToolBar * toolBar)641 bool CompilerGCC::BuildToolBar(wxToolBar* toolBar)
642 {
643     if (!IsAttached() || !toolBar)
644         return false;
645 
646     m_pTbar = toolBar;
647     Manager::Get()->AddonToolBar(toolBar, _T("compiler_toolbar"));
648     m_pToolTarget = XRCCTRL(*toolBar, "idToolTarget", wxChoice);
649     toolBar->Realize();
650     toolBar->SetInitialSize();
651     DoRecreateTargetMenu(); // make sure the tool target combo is up-to-date
652     return true;
653 }
654 
Dispatcher(wxCommandEvent & event)655 void CompilerGCC::Dispatcher(wxCommandEvent& event)
656 {
657     int eventId = event.GetId();
658 
659 //    Manager::Get()->GetMessageManager()->Log(wxT("Dispatcher")));
660 
661     if (eventId == idMenuRun)
662         OnRun(event);
663     else if (eventId == idMenuCompileAndRun)
664         OnCompileAndRun(event);
665     else if (eventId == idMenuCompile)
666         OnCompile(event);
667     else if (eventId == idMenuCompileFromProjectManager)
668         OnCompile(event);
669     else if (eventId == idMenuCompileFile)
670         OnCompileFile(event);
671     else if (eventId == idMenuCompileFileFromProjectManager)
672         OnCompileFile(event);
673     else if (eventId == idMenuCleanFileFromProjectManager)
674         OnCleanFile(event);
675     else if (eventId == idMenuRebuild)
676         OnRebuild(event);
677     else if (eventId == idMenuRebuildFromProjectManager)
678         OnRebuild(event);
679     else if (eventId == idMenuBuildWorkspace)
680         OnCompileAll(event);
681     else if (eventId == idMenuRebuildWorkspace)
682         OnRebuildAll(event);
683     else if (   eventId == idMenuProjectCompilerOptions
684              || eventId == idMenuProjectCompilerOptionsFromProjectManager )
685         OnProjectCompilerOptions(event);
686     else if (eventId == idMenuTargetCompilerOptions)
687         OnTargetCompilerOptions(event);
688     else if (eventId == idMenuClean)
689         OnClean(event);
690     else if (eventId == idMenuCleanWorkspace)
691         OnCleanAll(event);
692     else if (eventId == idMenuCleanFromProjectManager)
693         OnClean(event);
694     else if (eventId == idMenuKillProcess)
695         OnKillProcess(event);
696     else if (eventId == idMenuNextError)
697         OnNextError(event);
698     else if (eventId == idMenuPreviousError)
699         OnPreviousError(event);
700     else if (eventId == idMenuClearErrors)
701         OnClearErrors(event);
702     else if (eventId == idMenuSettings)
703         OnConfig(event);
704 
705     // Return focus to current editor
706     cbEditor* ed = 0;
707     if ( (ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor()) )
708         ed->GetControl()->SetFocus();
709 }
710 
TextURL(wxTextUrlEvent & event)711 void CompilerGCC::TextURL(wxTextUrlEvent& event)
712 {
713     if (event.GetId() == idBuildLog && event.GetMouseEvent().ButtonDown(wxMOUSE_BTN_LEFT))
714         m_pLog->OpenLink(event.GetURLStart(), event.GetURLEnd());
715     else
716         event.Skip();
717 }
718 
SetupEnvironment()719 void CompilerGCC::SetupEnvironment()
720 {
721     // Special case so "No Compiler" is valid, but I'm not sure there is
722     // any valid reason to continue with this function.
723     // If we do continue there are wx3 asserts, because of empty paths.
724     if (m_CompilerId == wxT("null"))
725         return;
726 
727     Compiler* compiler = CompilerFactory::GetCompiler(m_CompilerId);
728     if (!compiler)
729         return;
730 
731     wxString currentPath;
732     if ( !wxGetEnv(_T("PATH"), &currentPath) )
733     {
734         InfoWindow::Display(_("Environment error"),
735                             _("Could not read the PATH environment variable!\n"
736                               "This can't be good. There may be problems running\n"
737                               "system commands and the application might not behave\n"
738                               "the way it was designed to..."),
739                             15000, 3000);
740         return;
741     }
742 
743 //    Manager::Get()->GetLogManager()->DebugLogError(_T("PATH environment:"));
744 //    Manager::Get()->GetLogManager()->DebugLogError(currentPath);
745 
746     const wxString pathApp  = platform::windows ? _T(";") : _T(":");
747     const wxString pathSep  = wxFileName::GetPathSeparator(); // "\" or "/"
748     const bool     caseSens = !(platform::windows);
749 
750     wxString      cApp       = compiler->GetPrograms().C;
751     Manager::Get()->GetMacrosManager()->ReplaceMacros(cApp);
752     wxArrayString extraPaths = compiler->GetExtraPaths();
753     wxString      extraPathsBinPath(wxEmptyString);
754 
755     // Get configured masterpath, expand macros and remove trailing separators
756     wxString masterPath = compiler->GetMasterPath();
757 
758     Manager::Get()->GetMacrosManager()->ReplaceMacros(masterPath);
759     while (   !masterPath.IsEmpty()
760            && ((masterPath.Last() == '\\') || (masterPath.Last() == '/')) )
761         masterPath.RemoveLast();
762 
763     // Compile new PATH list...
764     wxPathList pathList;
765     // [1] Pre-pend "master path" and "master path\bin"...
766     if ( !masterPath.Trim().IsEmpty() ) // Would be very bad, if it *is* empty
767     {
768         pathList.Add(masterPath + pathSep + _T("bin"));
769         pathList.Add(masterPath); // in case there is no "bin" sub-folder
770     }
771     // [2] Get configured extrapath(s), expand macros and remove trailing separators
772     for (size_t i=0; i<extraPaths.GetCount(); ++i)
773     {
774         wxString extraPath = extraPaths[i];
775         Manager::Get()->GetMacrosManager()->ReplaceMacros(extraPath);
776         while (extraPath.Last() == '\\' || extraPath.Last() == '/')
777             extraPath.RemoveLast();
778         if (!extraPath.Trim().IsEmpty())
779         {
780             // Remember, if we found the C application in the extra path's:
781             if (   extraPathsBinPath.IsEmpty()
782                 && wxFileExists(extraPath + pathSep + cApp ) )
783                 extraPathsBinPath = extraPath;
784             pathList.Add(extraPath);
785         }
786     }
787     // [3] Append what has already been in the PATH envvar...
788     // If we do it this way, paths are automatically normalized and doubles are removed
789     wxPathList pathArray;
790     pathArray.AddEnvList(_T("PATH"));
791     pathList.Add(pathArray);
792 
793     // Try to locate the path to the C compiler:
794     wxString binPath = pathList.FindAbsoluteValidPath(cApp);
795 
796     // It seems, under Win32, the above command doesn't search in paths with spaces...
797     // Look directly for the file in question in masterPath if it is not already found.
798     if (    binPath.IsEmpty()
799         || (pathList.Index(wxPathOnly(binPath), caseSens)==wxNOT_FOUND) )
800     {
801         if      (wxFileExists(masterPath + pathSep + _T("bin") + pathSep + cApp))
802             binPath = masterPath + pathSep + _T("bin");
803         else if (wxFileExists(masterPath + pathSep + cApp))
804             binPath = masterPath;
805         else if (!extraPathsBinPath.IsEmpty())
806             binPath = extraPathsBinPath;
807     }
808     else
809         binPath = wxPathOnly(binPath);
810 
811     /* TODO (jens#1#): Is the above correct ?
812        Or should we search in the whole systempath (pathList in this case) for the executable? */
813     // Try again...
814     if ((binPath.IsEmpty() || (pathList.Index(binPath, caseSens)==wxNOT_FOUND)))
815     {
816         InfoWindow::Display(_("Environment error"),
817                             _("Can't find compiler executable in your configured search path's for ") + compiler->GetName() + _T('\n'));
818         Manager::Get()->GetLogManager()->DebugLogError(F(_T("Can't find compiler executable in your configured search path's (for %s)..."), compiler->GetName().wx_str()));
819 
820         return; // Failed to locate compiler executable in path's as provided!
821     }
822 
823     // Convert the pathList into a string to apply.
824     wxString envPath(binPath); // make sure the bin-path we found is in front
825     // and remove it from pathList
826     pathList.Remove(binPath);
827     for (size_t i=0; i<pathList.GetCount(); ++i)
828         envPath += ( pathApp + pathList[i] );
829 
830 //    Manager::Get()->GetLogManager()->DebugLogError(_T("Updating compiler PATH environment:"));
831 //    Manager::Get()->GetLogManager()->DebugLogError(envPath);
832 
833     if ( !wxSetEnv(_T("PATH"), envPath) )
834     {
835         InfoWindow::Display(_("Environment error"),
836                             _("Can't set PATH environment variable! That's bad and the compiler might not work."));
837         Manager::Get()->GetLogManager()->DebugLog(_T("Can't set PATH environment variable! That's bad and the compiler might not work.\n"));
838     }
839 }
840 
StopRunningDebugger()841 bool CompilerGCC::StopRunningDebugger()
842 {
843     cbDebuggerPlugin *dbg = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
844     // is the debugger running?
845     if (dbg && dbg->IsRunning())
846     {
847         int ret = cbMessageBox(_("The debugger must be stopped to do a (re-)build.\n"
848                                  "Do you want to stop the debugger now?"),
849                                  _("Information"),
850                                 wxYES_NO | wxCANCEL | wxICON_QUESTION);
851         switch (ret)
852         {
853             case wxID_YES:
854             {
855                 m_pLog->Clear();
856                 Manager::Get()->GetLogManager()->Log(_("Stopping debugger..."), m_PageIndex);
857                 dbg->Stop();
858                 break;
859             }
860             case wxID_NO: // fall through
861             default:
862                 Manager::Get()->GetLogManager()->Log(_("Aborting (re-)build."), m_PageIndex);
863                 return false;
864         }
865     }
866 
867     return true;
868 }
869 
SaveOptions()870 void CompilerGCC::SaveOptions()
871 {
872     // save compiler sets
873     CompilerFactory::SaveSettings();
874 }
875 
LoadOptions()876 void CompilerGCC::LoadOptions()
877 {
878     // load compiler sets
879     CompilerFactory::LoadSettings();
880 }
881 
DoRegisterCompilers()882 void CompilerGCC::DoRegisterCompilers()
883 {
884     bool nonPlatComp = Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/non_plat_comp"), false);
885 
886     // register built-in compilers
887     CompilerFactory::RegisterCompiler(new CompilerMINGW);
888     if (platform::windows || nonPlatComp)
889     {
890         CompilerFactory::RegisterCompiler(new CompilerMSVC);
891         CompilerFactory::RegisterCompiler(new CompilerMSVC8);
892         CompilerFactory::RegisterCompiler(new CompilerMSVC10);
893         CompilerFactory::RegisterCompiler(new CompilerOW);
894         CompilerFactory::RegisterCompiler(new CompilerCYGWIN);
895         CompilerFactory::RegisterCompiler(new CompilerLCC);
896         CompilerFactory::RegisterCompiler(new CompilerKeilC51);
897         CompilerFactory::RegisterCompiler(new CompilerKeilCX51);
898         CompilerFactory::RegisterCompiler(new CompilerIAR(wxT("8051")));
899         CompilerFactory::RegisterCompiler(new CompilerIAR(wxT("ARM")));
900     }
901     CompilerFactory::RegisterCompiler(new CompilerICC);
902     CompilerFactory::RegisterCompiler(new CompilerGDC);
903     CompilerFactory::RegisterCompiler(new CompilerGNUFortran);
904     CompilerFactory::RegisterCompiler(new CompilerG95);
905     if (platform::windows || platform::Linux || nonPlatComp)
906         CompilerFactory::RegisterCompiler(new CompilerGNUARM);
907 
908     // register pure XML compilers
909     // user paths first
910     wxDir dir;
911     wxString filename;
912     wxArrayString compilers;
913     wxString path = ConfigManager::GetFolder(sdDataUser) + wxT("/compilers/");
914     if (wxDirExists(path) && dir.Open(path))
915     {
916         bool ok = dir.GetFirst(&filename, wxT("compiler_*.xml"), wxDIR_FILES);
917         while (ok)
918         {
919             compilers.Add(path + filename);
920             ok = dir.GetNext(&filename);
921         }
922     }
923     // global paths next
924     path = ConfigManager::GetFolder(sdDataGlobal) + wxT("/compilers/");
925     if (wxDirExists(path) && dir.Open(path))
926     {
927         bool ok = dir.GetFirst(&filename, wxT("compiler_*.xml"), wxDIR_FILES);
928         while (ok)
929         {
930             for (size_t i = 0; i < compilers.GetCount(); ++i)
931             {
932                 if (compilers[i].EndsWith(filename))
933                 {
934                     ok = false;
935                     break;
936                 }
937             }
938             if (ok) // user compilers of the same name take precedence
939                 compilers.Add(path + filename);
940             ok = dir.GetNext(&filename);
941         }
942     }
943     for (size_t i = 0; i < compilers.GetCount(); ++i)
944     {
945         wxXmlDocument compiler;
946         if (!compiler.Load(compilers[i]) || compiler.GetRoot()->GetName() != wxT("CodeBlocks_compiler"))
947             Manager::Get()->GetLogManager()->Log(_("Error: Invalid Code::Blocks compiler definition '") + compilers[i] + wxT("'."));
948         else
949         {
950             bool val = true;
951             wxString test;
952             if (!nonPlatComp && compiler.GetRoot()->GetAttribute(wxT("platform"), &test))
953             {
954                 if (test == wxT("windows"))
955                     val = platform::windows;
956                 else if (test == wxT("macosx"))
957                     val = platform::macosx;
958                 else if (test == wxT("linux"))
959                     val = platform::Linux;
960                 else if (test == wxT("freebsd"))
961                     val = platform::freebsd;
962                 else if (test == wxT("netbsd"))
963                     val = platform::netbsd;
964                 else if (test == wxT("openbsd"))
965                     val = platform::openbsd;
966                 else if (test == wxT("darwin"))
967                     val = platform::darwin;
968                 else if (test == wxT("solaris"))
969                     val = platform::solaris;
970                 else if (test == wxT("unix"))
971                     val = platform::Unix;
972             }
973             if (val)
974                 CompilerFactory::RegisterCompiler(
975                                    new CompilerXML(compiler.GetRoot()->GetAttribute(wxT("name"), wxEmptyString),
976                                                    compiler.GetRoot()->GetAttribute(wxT("id"), wxEmptyString),
977                                                    compilers[i]));
978         }
979     }
980 
981     // register (if any) user-copies of built-in compilers
982     CompilerFactory::RegisterUserCompilers();
983 }
984 
GetCurrentCompilerID()985 const wxString& CompilerGCC::GetCurrentCompilerID()
986 {
987     static wxString def = wxEmptyString;//_T("gcc");
988     return CompilerFactory::GetCompiler(m_CompilerId) ? m_CompilerId : def;
989 }
990 
SwitchCompiler(const wxString & id)991 void CompilerGCC::SwitchCompiler(const wxString& id)
992 {
993     if (!CompilerFactory::GetCompiler(id))
994         return;
995 
996     m_CompilerId = id;
997 
998     SetupEnvironment();
999 }
1000 
PrepareCompileFilePM(wxFileName & file)1001 void CompilerGCC::PrepareCompileFilePM(wxFileName& file)
1002 {
1003     // we 're called from a menu in ProjectManager
1004     // let's check the selected project...
1005     FileTreeData* ftd = DoSwitchProjectTemporarily();
1006     ProjectFile* pf = ftd->GetProjectFile();
1007     if (!pf)
1008         return;
1009 
1010     file = pf->file;
1011     CheckProject();
1012 }
1013 
PrepareCompileFile(wxFileName & file)1014 void CompilerGCC::PrepareCompileFile(wxFileName& file)
1015 {
1016     cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
1017     if (ed)
1018     {
1019         // make sure it is saved
1020         ed->Save();
1021         file.Assign(ed->GetFilename());
1022     }
1023 
1024     // Now activate the project this file belongs to
1025     ProjectFile* pf = ed->GetProjectFile();
1026     if (pf)
1027     {
1028         cbProject* CurProject = pf->GetParentProject();
1029         if (CurProject)
1030         {
1031             Manager::Get()->GetProjectManager()->SetProject(CurProject, true);
1032             CheckProject();
1033         }
1034     }
1035 }
1036 
CheckProject()1037 bool CompilerGCC::CheckProject()
1038 {
1039     AskForActiveProject();
1040 
1041     // switch compiler for the project (if needed)
1042     if      ( m_pProject && m_pProject->GetCompilerID() != m_CompilerId)
1043         SwitchCompiler(m_pProject->GetCompilerID());
1044     // switch compiler for single file (if needed)
1045     else if (!m_pProject && m_CompilerId != CompilerFactory::GetDefaultCompilerID())
1046         SwitchCompiler(CompilerFactory::GetDefaultCompilerID());
1047 
1048     return (m_pProject != 0L);
1049 }
1050 
AskForActiveProject()1051 void CompilerGCC::AskForActiveProject()
1052 {
1053     m_pProject = m_pBuildingProject
1054                 ? m_pBuildingProject
1055                 : Manager::Get()->GetProjectManager()->GetActiveProject();
1056 }
1057 
StartCompileFile(wxFileName file)1058 void CompilerGCC::StartCompileFile(wxFileName file)
1059 {
1060     if (m_pProject)
1061     {
1062         if (!m_pProject->SaveAllFiles())
1063             Manager::Get()->GetLogManager()->Log(_("Could not save all files..."));
1064 
1065         file.MakeRelativeTo(m_pProject->GetBasePath());
1066     }
1067 
1068     wxString fname = file.GetFullPath();
1069     if (!fname.IsEmpty())
1070     {
1071         CodeBlocksLogEvent evtSwitch(cbEVT_SWITCH_TO_LOG_WINDOW, m_pLog);
1072         Manager::Get()->ProcessEvent(evtSwitch);
1073 
1074         CompileFile( UnixFilename(fname) );
1075     }
1076 }
1077 
ProjectMakefile()1078 wxString CompilerGCC::ProjectMakefile()
1079 {
1080     AskForActiveProject();
1081 
1082     if (!m_pProject)
1083         return wxEmptyString;
1084 
1085     return m_pProject->GetMakefile();
1086 }
1087 
ClearLog(bool switchToLog)1088 void CompilerGCC::ClearLog(bool switchToLog)
1089 {
1090     if (m_IsWorkspaceOperation)
1091         return;
1092 
1093     if (IsProcessRunning())
1094         return;
1095 
1096     if (switchToLog)
1097     {
1098         CodeBlocksLogEvent evtSwitch(cbEVT_SWITCH_TO_LOG_WINDOW, m_pLog);
1099         Manager::Get()->ProcessEvent(evtSwitch);
1100     }
1101 
1102     if (m_pLog)
1103         m_pLog->Clear();
1104 }
1105 
DoSwitchProjectTemporarily()1106 FileTreeData* CompilerGCC::DoSwitchProjectTemporarily()
1107 {
1108     ProjectManager* manager = Manager::Get()->GetProjectManager();
1109     wxTreeCtrl* tree = manager->GetUI().GetTree();
1110     wxTreeItemId sel = manager->GetUI().GetTreeSelection();
1111     FileTreeData* ftd = sel.IsOk() ? (FileTreeData*)tree->GetItemData(sel) : nullptr;
1112     if (!ftd)
1113         return nullptr;
1114     // We're not rebuilding the tree, so the ftd pointer is still valid after the call.
1115     Manager::Get()->GetProjectManager()->SetProject(ftd->GetProject(), false);
1116     AskForActiveProject();
1117 
1118     return ftd;
1119 }
1120 
AddToCommandQueue(const wxArrayString & commands)1121 void CompilerGCC::AddToCommandQueue(const wxArrayString& commands)
1122 {
1123     ProjectBuildTarget* bt = m_pBuildingProject ? m_pBuildingProject->GetBuildTarget(GetTargetIndexFromName(m_pBuildingProject, m_BuildingTargetName)) : 0;
1124     m_CurrentProgress = 0;
1125     m_MaxProgress = 0;
1126     bool isLink = false;
1127     bool mustWait = false;
1128     size_t count = commands.GetCount();
1129     for (size_t i = 0; i < count; ++i)
1130     {
1131         wxString cmd = commands[i];
1132 
1133         // logging
1134         if (cmd.StartsWith(COMPILER_SIMPLE_LOG))
1135         {
1136             cmd.Remove(0, COMPILER_SIMPLE_LOG.Length());
1137             m_CommandQueue.Add(new CompilerCommand(wxEmptyString, cmd, m_pBuildingProject, bt));
1138         }
1139         // compiler change
1140         else if (cmd.StartsWith(COMPILER_TARGET_CHANGE))
1141         {
1142             ; // nothing to do for now
1143         }
1144         else if (cmd.StartsWith(COMPILER_WAIT))
1145         {
1146             mustWait = true;
1147         }
1148         else if (cmd.StartsWith(COMPILER_WAIT_LINK))
1149         {
1150             isLink = true;
1151         }
1152         else
1153         {
1154             // compiler command
1155             CompilerCommand* p = new CompilerCommand(cmd, wxEmptyString, m_pBuildingProject, bt);
1156             p->mustWait = mustWait;
1157             p->isLink = isLink;
1158             m_CommandQueue.Add(p);
1159             isLink = false;
1160             mustWait = false;
1161             ++m_MaxProgress;
1162         }
1163     }
1164 
1165     if (m_pLog->progress)
1166     {
1167         m_pLog->progress->SetRange(m_MaxProgress);
1168         m_pLog->progress->SetValue(m_CurrentProgress);
1169     }
1170 }
1171 
AllocProcesses()1172 void CompilerGCC::AllocProcesses()
1173 {
1174     // create the parallel processes array
1175     size_t parallel_processes = Manager::Get()->GetConfigManager(_T("compiler"))->ReadInt(_T("/parallel_processes"), 0);
1176     if (parallel_processes == 0)
1177         parallel_processes = std::max(1, wxThread::GetCPUCount());
1178     m_CompilerProcessList.resize(parallel_processes);
1179     for (CompilerProcess &p : m_CompilerProcessList)
1180     {
1181         p.pProcess = nullptr;
1182         p.PID = 0;
1183     }
1184 }
1185 
FreeProcesses()1186 void CompilerGCC::FreeProcesses()
1187 {
1188     // free the parallel processes array
1189     for (CompilerProcess &p : m_CompilerProcessList)
1190         Delete(p.pProcess);
1191     m_CompilerProcessList.clear();
1192 }
1193 
ReAllocProcesses()1194 bool CompilerGCC::ReAllocProcesses()
1195 {
1196     FreeProcesses();
1197     AllocProcesses();
1198     return true;
1199 }
1200 
IsProcessRunning(int idx) const1201 bool CompilerGCC::IsProcessRunning(int idx) const
1202 {
1203     // invalid process index
1204     if (m_CompilerProcessList.empty() || idx >= (int)m_CompilerProcessList.size())
1205         return false;
1206 
1207     // specific process
1208     if (idx >= 0)
1209         return (m_CompilerProcessList.at(static_cast<size_t>(idx)).pProcess != 0);
1210 
1211     // any process (idx = -1)
1212     for (const CompilerProcess &p : m_CompilerProcessList)
1213     {
1214         if (p.pProcess)
1215             return true;
1216     }
1217     return false;
1218 }
1219 
GetNextAvailableProcessIndex() const1220 int CompilerGCC::GetNextAvailableProcessIndex() const
1221 {
1222     for (size_t i = 0; i < m_CompilerProcessList.size(); ++i)
1223     {
1224         const CompilerProcess &p = m_CompilerProcessList[i];
1225         if (!p.pProcess && p.PID == 0)
1226             return i;
1227     }
1228     return -1;
1229 }
1230 
GetActiveProcessCount() const1231 int CompilerGCC::GetActiveProcessCount() const
1232 {
1233     int count = 0;
1234     for (const CompilerProcess &p : m_CompilerProcessList)
1235     {
1236         if (p.pProcess)
1237             ++count;
1238     }
1239     return count;
1240 }
1241 
DoRunQueue()1242 int CompilerGCC::DoRunQueue()
1243 {
1244     // leave if already running
1245     int procIndex = GetNextAvailableProcessIndex();
1246     if (procIndex == -1)
1247         return -2;
1248 
1249     // if next command is linking and compilation is still in progress, abort
1250     if (IsProcessRunning())
1251     {
1252         CompilerCommand* cmd = m_CommandQueue.Peek();
1253         if (cmd && (cmd->mustWait || cmd->isLink))
1254             return -3;
1255     }
1256 
1257     CompilerCommand* cmd = m_CommandQueue.Next();
1258     if (!cmd)
1259     {
1260         if (IsProcessRunning())
1261             return 0;
1262 
1263         while (1)
1264         {
1265             // keep switching build states until we have commands to run or reach end of states
1266             BuildStateManagement();
1267             cmd = m_CommandQueue.Next();
1268             if (!cmd && m_BuildState == bsNone && m_NextBuildState == bsNone)
1269             {
1270                 NotifyJobDone(true);
1271                 ResetBuildState();
1272                 if (m_RunAfterCompile)
1273                 {
1274                     m_RunAfterCompile = false;
1275                     if (Run() == 0)
1276                         DoRunQueue();
1277                 }
1278                 return 0;
1279             }
1280 
1281             if (cmd)
1282                 break;
1283         }
1284     }
1285 
1286     wxString dir = cmd->dir;
1287 
1288     // log file
1289     bool hasLog = Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/save_html_build_log"), false);
1290     bool saveFull = Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/save_html_build_log/full_command_line"), false);
1291     if (hasLog)
1292     {
1293         if (!cmd->command.IsEmpty() && saveFull)
1294             LogMessage(cmd->command, cltNormal, ltFile);
1295         else if (!cmd->message.IsEmpty() && !saveFull)
1296             LogMessage(cmd->message, cltNormal, ltFile);
1297     }
1298 
1299     if (cmd->command.IsEmpty())
1300     {
1301         // log message
1302         if (!cmd->message.IsEmpty())
1303             LogMessage(cmd->message, cltNormal, ltMessages, false, false, true);
1304 
1305         int ret = DoRunQueue();
1306         delete cmd;
1307         return ret;
1308     }
1309     else if (cmd->command.StartsWith(_T("#run_script")))
1310     {
1311         // log message
1312         if (!cmd->message.IsEmpty())
1313             LogMessage(cmd->message, cltNormal, ltMessages, false, false, true);
1314 
1315         // special "run_script" command
1316         wxString script = cmd->command.AfterFirst(_T(' '));
1317         if (script.IsEmpty())
1318         {
1319             wxString msg = _("The #run_script command must be followed by a script filename");
1320             LogMessage(msg, cltError);
1321         }
1322         else
1323         {
1324             Manager::Get()->GetMacrosManager()->ReplaceMacros(script);
1325             wxString msg = _("Running script: ") + script;
1326             LogMessage(msg);
1327 
1328             Manager::Get()->GetScriptingManager()->LoadScript(script);
1329         }
1330         int ret = DoRunQueue();
1331         delete cmd;
1332         return ret;
1333     }
1334 
1335     wxString oldLibPath; // keep old PATH/LD_LIBRARY_PATH contents
1336     wxGetEnv(CB_LIBRARY_ENVVAR, &oldLibPath);
1337 
1338     bool pipe = true;
1339     int flags = wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER;
1340     if (cmd->isRun)
1341     {
1342         pipe = false; // no need to pipe output channels...
1343         flags |= wxEXEC_NOHIDE;
1344         dir = m_CdRun;
1345 
1346         // setup dynamic linker path
1347         wxString newLibPath = cbGetDynamicLinkerPathForTarget(m_pProject, cmd->target);
1348         newLibPath = cbMergeLibPaths(oldLibPath, newLibPath);
1349         wxSetEnv(CB_LIBRARY_ENVVAR, newLibPath);
1350         LogMessage(wxString(_("Set variable: ")) + CB_LIBRARY_ENVVAR wxT("=") + newLibPath, cltInfo);
1351     }
1352 
1353     // log message here, so the logging for run executable commands is done after the log message
1354     // for set variable.
1355     if (!cmd->message.IsEmpty())
1356         LogMessage(cmd->message, cltNormal, ltMessages, false, false, true);
1357 
1358     // special shell used only for build commands
1359     if (!cmd->isRun)
1360     {
1361         ExpandBackticks(cmd->command);
1362 
1363         // Run the command in a shell, so stream redirections (<, >, << and >>),
1364         // piping and other shell features can be evaluated.
1365         if (!platform::windows)
1366         {
1367             wxString shell = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/console_shell"), DEFAULT_CONSOLE_SHELL);
1368             cmd->command = shell + _T(" '") + cmd->command + _T("'");
1369         }
1370     }
1371 
1372     // create a new process
1373     CompilerProcess &process = m_CompilerProcessList.at(procIndex);
1374     process.OutputFile = (cmd->isLink && cmd->target) ? cmd->target->GetOutputFilename() : wxString(wxEmptyString);
1375     process.pProcess = new PipedProcess(&(process.pProcess), this, idGCCProcess, pipe, dir, procIndex);
1376 
1377     process.PID = wxExecute(cmd->command, flags, process.pProcess);
1378     if (!process.PID)
1379     {
1380         wxString err = wxString::Format(_("Execution of '%s' in '%s' failed."),
1381                                         cmd->command.wx_str(), wxGetCwd().wx_str());
1382         LogMessage(err, cltError);
1383         LogWarningOrError(cltError, 0, wxEmptyString, wxEmptyString, err);
1384         if (!m_CommandQueue.LastCommandWasRun())
1385         {
1386             if ( !IsProcessRunning() )
1387             {
1388                 wxString msg = wxString::Format(_("%s (%s)"), GetErrWarnStr().wx_str(), GetMinSecStr().wx_str());
1389                 LogMessage(msg, cltError, ltAll, true);
1390                 LogWarningOrError(cltNormal, 0, wxEmptyString, wxEmptyString,
1391                                   wxString::Format(_("=== Build failed: %s ==="), msg.wx_str()));
1392                 m_pListLog->AutoFitColumns(2);
1393                 SaveBuildLog();
1394             }
1395             if (!Manager::IsBatchBuild() && m_pLog->progress)
1396                 m_pLog->progress->SetValue(0);
1397         }
1398         Delete(process.pProcess);
1399         m_CommandQueue.Clear();
1400         ResetBuildState();
1401     }
1402     else
1403         m_timerIdleWakeUp.Start(100);
1404 
1405     // restore dynamic linker path
1406     wxSetEnv(CB_LIBRARY_ENVVAR, oldLibPath);
1407 
1408     delete cmd;
1409     return DoRunQueue();
1410 }
1411 
DoClearTargetMenu()1412 void CompilerGCC::DoClearTargetMenu()
1413 {
1414     if (m_TargetMenu)
1415     {
1416         bool foundFirstSeparator = false;
1417         wxMenuItemList& items = m_TargetMenu->GetMenuItems();
1418         for (wxMenuItemList::iterator it = items.begin(); it != items.end(); )
1419         {
1420             wxMenuItem *item = *it;
1421             // Make sure we increment valid iterator (Delete will invalidate it).
1422             ++it;
1423             if (item)
1424             {
1425                 if (item->GetKind() == wxITEM_SEPARATOR)
1426                 {
1427                     if (!foundFirstSeparator)
1428                         foundFirstSeparator = true;
1429                 }
1430                 // Delete menu items only after the first separator.
1431                 // We do this because we don't want to delete the first item, because we want to
1432                 // make it possible for users to assign keyboard shortcuts for it.
1433                 else if (foundFirstSeparator)
1434                     m_TargetMenu->Delete(item);
1435             }
1436         }
1437 // mandrav: The following lines DO NOT clear the menu!
1438 //        wxMenuItemList& items = m_TargetMenu->GetMenuItems();
1439 //        bool olddelete=items.GetDeleteContents();
1440 //        items.DeleteContents(true);
1441 //        items.Clear();
1442 //        items.DeleteContents(olddelete);
1443     }
1444 }
1445 
IsValidTarget(const wxString & target) const1446 bool CompilerGCC::IsValidTarget(const wxString &target) const
1447 {
1448     if ( target.IsEmpty() )
1449         return false;
1450     if ( m_Targets.Index(target) == -1 )
1451         return false;
1452     const ProjectBuildTarget* tgt = Manager::Get()->GetProjectManager()->GetActiveProject()->GetBuildTarget(target);
1453     if ( tgt && ! tgt->SupportsCurrentPlatform() )
1454         return false;
1455     return true;
1456 }
1457 
DoRecreateTargetMenu()1458 void CompilerGCC::DoRecreateTargetMenu()
1459 {
1460     if (!IsAttached())
1461         return;
1462 
1463     if (m_pToolTarget)
1464         m_pToolTarget->Freeze();
1465     wxMenuBar* mbar = Manager::Get()->GetAppFrame()->GetMenuBar();
1466     if (mbar)
1467         mbar->Freeze();
1468 
1469     do
1470     {
1471         // clear menu and combo
1472         DoClearTargetMenu();
1473         if (m_pToolTarget)
1474             m_pToolTarget->Clear();
1475 
1476         // if no project, leave
1477         if (!CheckProject())
1478             break;
1479 
1480         // if no targets, leave
1481         if (!m_Targets.GetCount())
1482             break;
1483 
1484         wxString tgtStr(m_pProject->GetFirstValidBuildTargetName());
1485 
1486         // find out the should-be-selected target
1487         if (cbWorkspace* wsp = Manager::Get()->GetProjectManager()->GetWorkspace())
1488         {
1489           const wxString preferredTarget = wsp->GetPreferredTarget();
1490           tgtStr = preferredTarget;
1491           if ( !IsValidTarget(tgtStr) )
1492               tgtStr = m_pProject->GetActiveBuildTarget();
1493           if ( !IsValidTarget(tgtStr) )
1494               tgtStr = m_pProject->GetFirstValidBuildTargetName(); // last-chance default
1495           if ( preferredTarget.IsEmpty() )
1496               wsp->SetPreferredTarget(tgtStr);
1497         }
1498 
1499 
1500         // fill the menu and combo
1501         for (int x = 0; x < int(m_Targets.size()); ++x)
1502         {
1503             if (m_TargetMenu && x < maxTargetInMenus)
1504             {
1505                 wxString help;
1506                 help.Printf(_("Build target '%s' in current project"), GetTargetString(x).wx_str());
1507                 m_TargetMenu->AppendCheckItem(idMenuSelectTargetOther[x], GetTargetString(x), help);
1508             }
1509             if (m_pToolTarget)
1510                 m_pToolTarget->Append(GetTargetString(x));
1511         }
1512 
1513         if (m_TargetMenu && int(m_Targets.size()) > maxTargetInMenus)
1514         {
1515             m_TargetMenu->Append(idMenuSelectTargetHasMore, _("More targets available..."),
1516                                  _("Use the select target menu item to see them!"));
1517             m_TargetMenu->Enable(idMenuSelectTargetHasMore, false);
1518         }
1519 
1520         // connect menu events
1521         Connect(idMenuSelectTargetOther[0], idMenuSelectTargetOther[maxTargetInMenus - 1],
1522                 wxEVT_COMMAND_MENU_SELECTED,
1523                 (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
1524                 &CompilerGCC::OnSelectTarget);
1525 
1526         // housekeeping
1527         m_TargetIndex = m_Targets.Index(tgtStr);
1528         m_RealTargetIndex = m_TargetIndex - m_RealTargetsStartIndex;
1529         if (m_RealTargetIndex < 0)
1530             m_RealTargetIndex = -1;
1531 
1532         DoUpdateTargetMenu(m_TargetIndex);
1533 
1534         // update combo
1535         if (m_pToolTarget)
1536             m_pToolTarget->SetSelection(m_TargetIndex);
1537 
1538         // finally, make sure we 're using the correct compiler for the project
1539         SwitchCompiler(m_pProject->GetCompilerID());
1540     }
1541     while (false);
1542 
1543     if (mbar)
1544         mbar->Thaw();
1545     if (m_pToolTarget)
1546         m_pToolTarget->Thaw();
1547 }
1548 
DoUpdateTargetMenu(int targetIndex)1549 void CompilerGCC::DoUpdateTargetMenu(int targetIndex)
1550 {
1551     // update indices
1552     m_TargetIndex = targetIndex;
1553     m_RealTargetIndex = m_TargetIndex - m_RealTargetsStartIndex;
1554     if (m_RealTargetIndex < 0)
1555         m_RealTargetIndex = -1;
1556 
1557     if (m_TargetIndex == -1)
1558         m_TargetIndex = 0;
1559 
1560     if (m_pProject)
1561         m_pProject->SetActiveBuildTarget(GetTargetString(m_TargetIndex));
1562 
1563     // update menu
1564     if (m_TargetMenu)
1565     {
1566         for (int i = 0; i < maxTargetInMenus; ++i)
1567         {
1568             wxMenuItem* item = m_TargetMenu->FindItem(idMenuSelectTargetOther[i]);
1569             if (!item || !item->IsCheckable())
1570                 continue;
1571             item->Check(i == m_TargetIndex);
1572         }
1573     }
1574 
1575     // the tool combo is updated in DoRecreateTargetMenu()
1576     // can't set it here, because this function is called by the
1577     // tool combo's event handler
1578 //    DBGLOG(_T("m_TargetIndex=%d, m_pToolTarget->GetCurrentSelection()=%d, m_RealTargetsStartIndex=%d"), m_TargetIndex, m_pToolTarget->GetCurrentSelection(), m_RealTargetsStartIndex);
1579 }
1580 
UpdateProjectTargets(cbProject * project)1581 void CompilerGCC::UpdateProjectTargets(cbProject* project)
1582 {
1583     m_Targets.Clear();
1584     if (!project)
1585         return;
1586 
1587     // update the list of targets (virtual + real)
1588     wxArrayString virtuals = project->GetVirtualBuildTargets();
1589     for (size_t i = 0; i < virtuals.GetCount(); ++i)
1590         m_Targets.Add(virtuals[i]);
1591 
1592     for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
1593     {
1594         ProjectBuildTarget *tgt = project->GetBuildTarget(i);
1595         if ( tgt->SupportsCurrentPlatform() )
1596             m_Targets.Add( tgt->GetTitle() );
1597     }
1598 
1599     // keep the index for the first real target
1600     m_RealTargetsStartIndex = virtuals.GetCount();
1601 
1602     // actually rebuild menu and combo
1603     DoRecreateTargetMenu();
1604 }
1605 
GetTargetString(int index)1606 wxString CompilerGCC::GetTargetString(int index)
1607 {
1608     if (index == -1)
1609         index = m_TargetIndex;
1610     if (index >= 0 && index < (int)m_Targets.GetCount())
1611         return m_Targets[index];
1612     return wxEmptyString;
1613 }
1614 
DoPrepareQueue(bool clearLog)1615 void CompilerGCC::DoPrepareQueue(bool clearLog)
1616 {
1617     if (m_CommandQueue.GetCount() == 0)
1618     {
1619         CodeBlocksEvent evt(cbEVT_COMPILER_STARTED, 0, m_pProject, 0, this);
1620         Manager::Get()->ProcessEvent(evt);
1621 
1622         if (clearLog)
1623         {
1624             ClearLog(true);
1625             DoClearErrors();
1626         }
1627         // wxStartTimer();
1628         m_StartTime = wxGetLocalTimeMillis();
1629     }
1630     Manager::Yield();
1631 }
1632 
NotifyCleanProject(const wxString & target)1633 void CompilerGCC::NotifyCleanProject(const wxString& target)
1634 {
1635     if (m_CommandQueue.GetCount() == 0)
1636     {
1637         CodeBlocksEvent evt(cbEVT_CLEAN_PROJECT_STARTED, 0, m_pProject, 0, this);
1638         evt.SetBuildTargetName(target);
1639         Manager::Get()->ProcessEvent(evt);
1640     }
1641     Manager::Yield();
1642 }
1643 
NotifyCleanWorkspace()1644 void CompilerGCC::NotifyCleanWorkspace()
1645 {
1646     if (m_CommandQueue.GetCount() == 0)
1647     {
1648         CodeBlocksEvent evt(cbEVT_CLEAN_WORKSPACE_STARTED, 0, 0, 0, this);
1649         Manager::Get()->ProcessEvent(evt);
1650     }
1651     Manager::Yield();
1652 }
1653 
DoAskForTarget()1654 ProjectBuildTarget* CompilerGCC::DoAskForTarget()
1655 {
1656     if (!CheckProject())
1657         return 0L;
1658 
1659     return m_pProject->GetBuildTarget(m_RealTargetIndex);
1660 }
1661 
DoGUIAskForTarget()1662 int CompilerGCC::DoGUIAskForTarget()
1663 {
1664     if (!CheckProject())
1665         return -1;
1666 
1667     return m_pProject->SelectTarget(m_RealTargetIndex);
1668 }
1669 
UseMake(cbProject * project)1670 bool CompilerGCC::UseMake(cbProject* project)
1671 {
1672     if (!project)
1673         project = m_pProject;
1674     if (!project)
1675         return false;
1676     wxString idx = project->GetCompilerID();
1677     if (CompilerFactory::GetCompiler(idx))
1678         return project->IsMakefileCustom();
1679 
1680     return false;
1681 }
1682 
GetCurrentCompilerID(ProjectBuildTarget * target)1683 wxString CompilerGCC::GetCurrentCompilerID(ProjectBuildTarget* target)
1684 {
1685     if (target)
1686         return target->GetCompilerID();
1687     if (m_pBuildingProject)
1688         return m_pBuildingProject->GetCompilerID();
1689     if (m_pProject)
1690         return m_pProject->GetCompilerID();
1691     return wxEmptyString;
1692 }
1693 
CompilerValid(ProjectBuildTarget * target)1694 auto CompilerGCC::CompilerValid(ProjectBuildTarget* target) -> CompilerValidResult
1695 {
1696     CompilerValidResult result;
1697     if (!target)
1698         result.compiler = CompilerFactory::GetDefaultCompiler();
1699     else
1700     {
1701         wxString idx = GetCurrentCompilerID(target);
1702         result.compiler = CompilerFactory::GetCompiler(idx);
1703     }
1704     if (result.compiler)
1705         result.isValid = result.compiler->IsValid();
1706     return result;
1707 }
1708 
PrintInvalidCompiler(ProjectBuildTarget * target,Compiler * compiler,const wxString & finalMessage)1709 void CompilerGCC::PrintInvalidCompiler(ProjectBuildTarget *target, Compiler *compiler, const wxString &finalMessage)
1710 {
1711     wxString compilerName, compilerName2(wxT("unknown"));
1712     if (compiler)
1713     {
1714         compilerName = wxT("(") + compiler->GetName() + wxT(") ");
1715         compilerName2 = compiler->GetName();
1716     }
1717 
1718     wxString title;
1719     if (target)
1720         title = target->GetFullTitle();
1721     else
1722         title = wxT("unknown");
1723 
1724     wxString msg;
1725     msg.Printf(_T("Project/Target: \"%s\":\n")
1726                _T("  The compiler's setup %sis invalid, so Code::Blocks cannot find/run the compiler.\n")
1727                _T("  Probably the toolchain path within the compiler options is not setup correctly?!\n")
1728                _T("  Do you have a compiler installed?\n")
1729                _T("Goto \"Settings->Compiler...->Global compiler settings->%s->Toolchain executables\"")
1730                _T(" and fix the compiler's setup.\n"),
1731                title.wx_str(), compilerName.wx_str(), compilerName2.wx_str());
1732 
1733     LogManager *logger = Manager::Get()->GetLogManager();
1734     logger->LogError(msg, m_PageIndex);
1735     if (compiler)
1736         logger->LogError(compiler->MakeInvalidCompilerMessages(), m_PageIndex);
1737     logger->LogError(finalMessage, m_PageIndex);
1738 }
1739 
PrintBanner(BuildAction action,cbProject * prj,ProjectBuildTarget * target)1740 void CompilerGCC::PrintBanner(BuildAction action, cbProject* prj, ProjectBuildTarget* target)
1741 {
1742     if (!CompilerValid(target).isValid)
1743         return;
1744 
1745     CodeBlocksLogEvent evtShow(cbEVT_SHOW_LOG_MANAGER);
1746     Manager::Get()->ProcessEvent(evtShow);
1747 
1748     if (!prj)
1749         prj = m_pProject;
1750 
1751     wxString Action;
1752     switch (action)
1753     {
1754     case baClean:
1755         Action = _("Clean");
1756         break;
1757     case baRun:
1758         Action = _("Run");
1759         break;
1760     case baBuildFile:
1761         Action = _("Build file");
1762         break;
1763     default:
1764     case baBuild:
1765         Action = _("Build");
1766         break;
1767     }
1768 
1769     wxString compilerName(_("unknown"));
1770     Compiler *compiler = CompilerFactory::GetCompiler(GetCurrentCompilerID(target));
1771     if (compiler)
1772         compilerName = compiler->GetName();
1773 
1774     wxString targetName = target ? target->GetTitle() : wxString(_("\"no target\""));
1775     wxString projectName = prj ? prj->GetTitle() : wxString(_("\"no project\""));
1776 
1777     wxString banner;
1778     banner.Printf(_("%s: %s in %s (compiler: %s)"),
1779                   Action.wx_str(), targetName.wx_str(), projectName.wx_str(), compilerName.wx_str());
1780     LogWarningOrError(cltNormal, 0, wxEmptyString, wxEmptyString, wxT("=== ") + banner + wxT(" ==="));
1781     LogMessage(wxT("-------------- ") + banner + wxT("---------------"), cltNormal, ltAll, false, true);
1782     m_pListLog->AutoFitColumns(2);
1783 }
1784 
DoGotoNextError()1785 void CompilerGCC::DoGotoNextError()
1786 {
1787     CodeBlocksLogEvent eventSwitchLog(cbEVT_SWITCH_TO_LOG_WINDOW, m_pListLog);
1788     Manager::Get()->ProcessEvent(eventSwitchLog);
1789 
1790     m_Errors.Next();
1791     m_pListLog->FocusError(m_Errors.GetFocusedError());
1792 }
1793 
DoGotoPreviousError()1794 void CompilerGCC::DoGotoPreviousError()
1795 {
1796     CodeBlocksLogEvent eventSwitchLog(cbEVT_SWITCH_TO_LOG_WINDOW, m_pListLog);
1797     Manager::Get()->ProcessEvent(eventSwitchLog);
1798 
1799     m_Errors.Previous();
1800     m_pListLog->FocusError(m_Errors.GetFocusedError());
1801 }
1802 
DoClearErrors()1803 void CompilerGCC::DoClearErrors()
1804 {
1805     m_Errors.Clear();
1806     m_pListLog->Clear();
1807     m_NotifiedMaxErrors = false;
1808 }
1809 
RunSingleFile(const wxString & filename)1810 int CompilerGCC::RunSingleFile(const wxString& filename)
1811 {
1812     wxFileName fname(filename);
1813 
1814     if (fname.GetExt() == _T("script"))
1815     {
1816         Manager::Get()->GetScriptingManager()->LoadScript(filename);
1817         return 0;
1818     }
1819 
1820     m_CdRun = fname.GetPath();
1821     fname.SetExt(FileFilters::EXECUTABLE_EXT);
1822     wxString exe_filename = fname.GetFullPath();
1823     wxString command;
1824 
1825     if (!platform::windows)
1826     {
1827         // for non-win platforms, use m_ConsoleTerm to run the console app
1828         wxString term = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/console_terminal"), DEFAULT_CONSOLE_TERM);
1829         term.Replace(_T("$TITLE"), _T("'") + exe_filename + _T("'"));
1830         command << term << strSPACE;
1831     }
1832 
1833     wxString baseDir = ConfigManager::GetExecutableFolder();
1834     wxString crunnStr = strQUOTE + baseDir + strSLASH + strCONSOLE_RUNNER + strQUOTE;
1835     if ( wxFileExists(baseDir + strSLASH + strCONSOLE_RUNNER) )
1836         command << crunnStr << strSPACE;
1837 
1838     if (!command.Replace(_T("$SCRIPT"), exe_filename))
1839         command << strQUOTE << exe_filename << strQUOTE; // if they didn't specify $SCRIPT, append:
1840 
1841     Manager::Get()->GetLogManager()->Log(_("Checking for existence: ") + exe_filename, m_PageIndex);
1842     if ( !wxFileExists(exe_filename) )
1843     {
1844         int ret = cbMessageBox(_("It seems that this file has not been built yet.\n"
1845                                  "Do you want to build it now?"),
1846                                 _("Information"),
1847                                 wxYES_NO | wxCANCEL | wxICON_QUESTION);
1848         switch (ret)
1849         {
1850             case wxID_YES:
1851             {
1852                 m_RunAfterCompile = true;
1853                 Build(wxEmptyString);
1854                 return -1;
1855             }
1856             case wxID_NO:
1857                 break;
1858             default:
1859                 return -1;
1860         }
1861     }
1862 
1863     Manager::Get()->GetMacrosManager()->ReplaceEnvVars(m_CdRun);
1864     Manager::Get()->GetLogManager()->Log(F(_("Executing: '%s' (in '%s')"), command.wx_str(), m_CdRun.wx_str()), m_PageIndex);
1865     m_CommandQueue.Add(new CompilerCommand(command, wxEmptyString, 0, 0, true));
1866     return 0;
1867 }
1868 
Run(const wxString & target)1869 int CompilerGCC::Run(const wxString& target)
1870 {
1871     if (!CheckProject())
1872         return -1;
1873     return Run(m_pProject->GetBuildTarget(target.IsEmpty() ? m_LastTargetName : target));
1874 }
1875 
Run(ProjectBuildTarget * target)1876 int CompilerGCC::Run(ProjectBuildTarget* target)
1877 {
1878     bool commandIsQuoted = false; // remember if we quoted the command, avoid unneeded quotes, because they break execution with "konsole" under KDE
1879     if (!CheckProject())
1880     {
1881         if (Manager::Get()->GetEditorManager()->GetActiveEditor())
1882             return RunSingleFile(Manager::Get()->GetEditorManager()->GetActiveEditor()->GetFilename());
1883         return -1;
1884     }
1885     else
1886     {
1887         target = m_pProject->GetBuildTarget(m_pProject->GetActiveBuildTarget());
1888     }
1889     PrintBanner(baRun, m_pProject, target);
1890 
1891     DoPrepareQueue(false);
1892     if (   !(target && (   target->GetTargetType() == ttCommandsOnly // do not require compiler for commands-only target
1893                         || target->GetCompilerID() == wxT("null") ))) // do not require compiler for "No Compiler" (why would you?)
1894     {
1895         CompilerValidResult result = CompilerValid(target);
1896         if (!result.isValid)
1897         {
1898             PrintInvalidCompiler(target, result.compiler, _("Run aborted..."));
1899             return -1;
1900         }
1901     }
1902 //    DBGLOG(_T("1) target=%s, m_RealTargetIndex=%d, m_TargetIndex=%d"), target ? target->GetTitle().c_str() : _T("null"), m_RealTargetIndex, m_TargetIndex);
1903 
1904     if (!target)
1905     {
1906         if (m_RealTargetIndex == -1) // only ask for target if a virtual target is selected
1907         {
1908             int idx = -1;
1909             int bak = m_RealTargetIndex;
1910             if (m_pProject->GetBuildTargetsCount() == 1)
1911                 idx = 0;
1912             else
1913                 idx = DoGUIAskForTarget();
1914 
1915             m_RealTargetIndex = idx;
1916             target = DoAskForTarget();
1917             m_RealTargetIndex = bak;
1918         }
1919         else
1920             target = DoAskForTarget();
1921     }
1922 //    DBGLOG(_T("2) target=%s, m_RealTargetIndex=%d, m_TargetIndex=%d"), target ? target->GetTitle().c_str() : _T("null"), m_RealTargetIndex, m_TargetIndex);
1923 
1924     if (!target)
1925         return -1;
1926 
1927     m_pProject->SetCurrentlyCompilingTarget(target); // help macros manager
1928 
1929     wxString out = UnixFilename(target->GetOutputFilename());
1930     Manager::Get()->GetMacrosManager()->ReplaceEnvVars(out);
1931 
1932     wxString cmd;
1933     wxString command;
1934     wxFileName f(out);
1935     f.MakeAbsolute(m_pProject->GetBasePath());
1936 
1937     m_CdRun = target->GetWorkingDir();
1938     Manager::Get()->GetMacrosManager()->ReplaceEnvVars(m_CdRun);
1939     wxFileName cd(m_CdRun);
1940     if (cd.IsRelative())
1941         cd.MakeAbsolute(m_pProject->GetBasePath());
1942     m_CdRun = cd.GetFullPath();
1943     wxString baseDir = ConfigManager::GetExecutableFolder();
1944 
1945     wxString titleStr = platform::windows
1946                       ? strQUOTE + m_pProject->GetTitle() + strQUOTE
1947                       : EscapeSpaces(m_pProject->GetTitle());
1948     wxString dirStr = platform::windows
1949                     ? strQUOTE + m_CdRun + strQUOTE
1950                     : EscapeSpaces(m_CdRun);
1951     wxString crunnStr = platform::windows
1952                       ? strQUOTE + baseDir + strSLASH + strCONSOLE_RUNNER + strQUOTE
1953                       : EscapeSpaces(baseDir + strSLASH + strCONSOLE_RUNNER);
1954     wxString hostapStr = platform::windows
1955                        ? strQUOTE + target->GetHostApplication() + strQUOTE
1956                        : EscapeSpaces(target->GetHostApplication());
1957     wxString execStr = platform::windows
1958                      ? strQUOTE + f.GetFullPath() + strQUOTE
1959                      : EscapeSpaces(f.GetFullPath());
1960 
1961     // for console projects, use helper app to wait for a key after
1962     // execution ends...
1963     if (target->GetTargetType() == ttConsoleOnly || target->GetRunHostApplicationInTerminal())
1964     {
1965         if (!platform::windows)
1966         {
1967             // for non-win platforms, use m_ConsoleTerm to run the console app
1968             wxString term = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/console_terminal"), DEFAULT_CONSOLE_TERM);
1969             term.Replace(_T("$TITLE"), titleStr);
1970             term.Replace(_T("$WORKDIR"), dirStr);
1971             cmd << term << strSPACE;
1972 
1973             wxString shell;
1974             wxGetEnv(_T("SHELL"), &shell);
1975             if (shell.Contains(_T("csh")))
1976             {
1977                 // "The csh is a tool utterly inadequate for programming,
1978                 //  and its use for such purposes should be strictly banned!"
1979                 //                 -- Csh Programming Considered Harmful
1980                 command << DEFAULT_CONSOLE_SHELL << strSPACE;
1981                 // each shell execution must be enclosed to "":
1982                 // xterm -T X -e /bin/sh -c "/usr/bin/cb_console_runner X"
1983                 // here is first \"
1984                 command << strQUOTE;
1985                 commandIsQuoted = true;
1986             }
1987         }
1988 
1989         if (target->GetUseConsoleRunner())
1990         {
1991             if (wxFileExists(baseDir + strSLASH + strCONSOLE_RUNNER))
1992             {
1993                 command << crunnStr << strSPACE;
1994 
1995                 if (!platform::windows)
1996                 {
1997                     // set LD_LIBRARY_PATH
1998                     command << CB_LIBRARY_ENVVAR << _T("=$") << CB_LIBRARY_ENVVAR << _T(':');
1999                     // we have to quote the string, just escape the spaces does not work
2000                     wxString strLinkerPath=cbGetDynamicLinkerPathForTarget(m_pProject, target);
2001                     QuoteStringIfNeeded(strLinkerPath);
2002                     command << strLinkerPath << strSPACE;
2003                 }
2004             }
2005         }
2006     }
2007 
2008     if (   target->GetTargetType() == ttDynamicLib
2009         || target->GetTargetType() == ttStaticLib )
2010     {
2011         // check for hostapp
2012         if (target->GetHostApplication().IsEmpty())
2013         {
2014             cbMessageBox(_("You must select a host application to \"run\" a library..."));
2015             m_pProject->SetCurrentlyCompilingTarget(0);
2016             return -1;
2017         }
2018         Manager::Get()->GetMacrosManager()->ReplaceEnvVars(hostapStr);
2019         command << hostapStr << strSPACE;
2020         command << target->GetExecutionParameters();
2021     }
2022     else if (target->GetTargetType() != ttCommandsOnly)
2023     {
2024         command << execStr << strSPACE;
2025         command << target->GetExecutionParameters();
2026         // each shell execution must be enclosed to "":
2027         // xterm -T X -e /bin/sh -c "/usr/bin/cb_console_runner X"
2028         // here is last \"
2029         if (commandIsQuoted)
2030             command << strQUOTE;
2031         Manager::Get()->GetMacrosManager()->ReplaceMacros(command, target);
2032         Manager::Get()->GetMacrosManager()->ReplaceEnvVars(command);
2033     }
2034     else
2035     {
2036         // commands-only target?
2037         if (target->GetHostApplication().IsEmpty())
2038         {
2039             cbMessageBox(_("You must select a host application to \"run\" a commands-only target..."));
2040             m_pProject->SetCurrentlyCompilingTarget(0);
2041             return -1;
2042         }
2043         command << hostapStr << strSPACE;
2044         command << target->GetExecutionParameters();
2045         Manager::Get()->GetMacrosManager()->ReplaceMacros(command, target);
2046         Manager::Get()->GetMacrosManager()->ReplaceEnvVars(command);
2047     }
2048 
2049     wxString script = command;
2050 
2051     if (platform::macosx)
2052     {
2053         if (target->GetTargetType() == ttConsoleOnly &&
2054             script.GetChar(0) == '\'' && script.GetChar(script.length()-1) == '\'')
2055         script = script.Mid(1,script.length()-2); // skip outmost single-quotes
2056 
2057         // convert embedded quotes to AppleScript syntax
2058         script.Replace(_T("\""), _T("\"&quote&\""), true);
2059         script.Replace(_T("\'"), _T("\"&ASCII character 39&\""), true);
2060     }
2061 
2062     if (!cmd.Replace(_T("$SCRIPT"), script))
2063         // if they didn't specify $SCRIPT, append:
2064         cmd << command;
2065 
2066     Manager::Get()->GetLogManager()->Log(_("Checking for existence: ") + f.GetFullPath(), m_PageIndex);
2067     if ( (target->GetTargetType() != ttCommandsOnly) && !wxFileExists(f.GetFullPath()) )
2068     {
2069         int ret = cbMessageBox(_("It seems that this project has not been built yet.\n"
2070                                  "Do you want to build it now?"),
2071                                _("Information"),
2072                                wxYES_NO | wxCANCEL | wxICON_QUESTION);
2073         switch (ret)
2074         {
2075             case wxID_YES:
2076             {
2077                 m_pProject->SetCurrentlyCompilingTarget(0);
2078                 m_RunAfterCompile = true;
2079                 Build(target);
2080                 return -1;
2081             }
2082             case wxID_NO:
2083                 break;
2084             default:
2085                 m_pProject->SetCurrentlyCompilingTarget(0);
2086                 return -1;
2087         }
2088     }
2089 
2090     const wxString &message = F(_("Executing: %s (in %s)"), cmd.wx_str(), m_CdRun.wx_str());
2091     m_CommandQueue.Add(new CompilerCommand(cmd, message, m_pProject, target, true));
2092 
2093     m_pProject->SetCurrentlyCompilingTarget(0);
2094 
2095     Manager::Get()->GetProjectManager()->SetIsRunning(this);
2096     return 0;
2097 }
2098 
GetMakeCommandFor(MakeCommand cmd,cbProject * project,ProjectBuildTarget * target)2099 wxString CompilerGCC::GetMakeCommandFor(MakeCommand cmd, cbProject* project, ProjectBuildTarget* target)
2100 {
2101     if (!project)
2102         return wxEmptyString;
2103 
2104     wxString compilerId = target ? target->GetCompilerID() : project->GetCompilerID();
2105     if (!CompilerFactory::IsValidCompilerID(compilerId))
2106         compilerId = CompilerFactory::GetDefaultCompilerID();
2107     wxString command = target && !target->GetMakeCommandFor(cmd).empty() ?
2108                        target->GetMakeCommandFor(cmd) : project->GetMakeCommandFor(cmd);
2109 
2110     Compiler* compiler = CompilerFactory::GetCompiler(compilerId);
2111     command.Replace(_T("$makefile"), project->GetMakefile());
2112     command.Replace(_T("$make"), compiler ? compiler->GetPrograms().MAKE : _T("make"));
2113     command.Replace(_T("$target"), target ? target->GetTitle() : _T(""));
2114     Manager::Get()->GetMacrosManager()->ReplaceMacros(command);
2115 
2116 //    Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("Make: %s"), command.c_str()));
2117     return command;
2118 }
2119 
DoClean(const wxArrayString & commands)2120 void CompilerGCC::DoClean(const wxArrayString& commands)
2121 {
2122     for (unsigned int i = 0; i < commands.GetCount(); ++i)
2123         if (wxFileExists(commands[i]))
2124             wxRemoveFile(commands[i]);
2125 }
2126 
Clean(ProjectBuildTarget * target)2127 int CompilerGCC::Clean(ProjectBuildTarget* target)
2128 {
2129     return Clean(target ? target->GetTitle() : _T(""));
2130 }
2131 
Clean(const wxString & target)2132 int CompilerGCC::Clean(const wxString& target)
2133 {
2134     m_LastBuildStep = true;
2135     return DoBuild(target, true, false);
2136 }
2137 
getBuildTargetName(const ProjectBuildTarget * bt)2138 static inline wxString getBuildTargetName(const ProjectBuildTarget *bt)
2139 {
2140     return bt ? bt->GetTitle() : wxString(_("<all targets>"));
2141 }
2142 
DoCleanWithMake(ProjectBuildTarget * bt)2143 bool CompilerGCC::DoCleanWithMake(ProjectBuildTarget* bt)
2144 {
2145     wxString cmd = GetMakeCommandFor(mcClean, m_pBuildingProject, bt);
2146     if (cmd.empty())
2147     {
2148         LogMessage(COMPILER_ERROR_LOG +
2149                    wxT("Make command for 'Clean project/target' is empty. Nothing will be cleaned!"),
2150                    cltError);
2151         return false;
2152     }
2153     Compiler* tgtCompiler = CompilerFactory::GetCompiler(bt->GetCompilerID());
2154     if (!tgtCompiler)
2155     {
2156         const wxString &message = F(_("Invalid compiler selected for target '%s'!"), getBuildTargetName(bt).wx_str());
2157 
2158         LogMessage(COMPILER_ERROR_LOG + message, cltError);
2159         return false;
2160     }
2161 
2162     bool showOutput = (tgtCompiler->GetSwitches().logging == clogFull);
2163 
2164     wxArrayString output, errors;
2165     wxSetWorkingDirectory(m_pBuildingProject->GetExecutionDir());
2166 
2167     ExpandBackticks(cmd);
2168     if (showOutput)
2169         LogMessage(F(_("Executing clean command: %s"), cmd.wx_str()), cltNormal);
2170 
2171     long result = wxExecute(cmd, output, errors, wxEXEC_SYNC);
2172     if (showOutput)
2173     {
2174         for(size_t i = 0; i < output.GetCount(); i++)
2175             LogMessage(F(_("%s"), output[i].wx_str()), cltNormal);
2176         for(size_t i = 0; i < errors.GetCount(); i++)
2177             LogMessage(F(_("%s"), errors[i].wx_str()), cltNormal);
2178     }
2179 
2180     return (result == 0);
2181 }
2182 
DistClean(const wxString & target)2183 int CompilerGCC::DistClean(const wxString& target)
2184 {
2185     if (!CheckProject())
2186         return -1;
2187     return DistClean(m_pProject->GetBuildTarget(target.IsEmpty() ? m_LastTargetName : target));
2188 }
2189 
DistClean(ProjectBuildTarget * target)2190 int CompilerGCC::DistClean(ProjectBuildTarget* target)
2191 {
2192     // make sure all project files are saved
2193     if (m_pProject && !m_pProject->SaveAllFiles())
2194         Manager::Get()->GetLogManager()->Log(_("Could not save all files..."));
2195 
2196     if (!m_IsWorkspaceOperation)
2197         DoPrepareQueue(true);
2198     if (!CompilerValid(target).isValid)
2199         return -1;
2200 
2201 //    Manager::Get()->GetMacrosManager()->Reset();
2202 
2203     if (m_pProject)
2204         wxSetWorkingDirectory(m_pProject->GetBasePath());
2205 
2206     if ( UseMake() )
2207     {
2208         wxString cmd = GetMakeCommandFor(mcDistClean, m_pProject, target);
2209         m_CommandQueue.Add(new CompilerCommand(cmd, wxEmptyString, m_pProject, target));
2210         return DoRunQueue();
2211     }
2212     else
2213     {
2214         NotImplemented(_T("CompilerGCC::DistClean() without a custom Makefile"));
2215         return -1;
2216     }
2217     return 0;
2218 }
2219 
InitBuildState(BuildJob job,const wxString & target)2220 void CompilerGCC::InitBuildState(BuildJob job, const wxString& target)
2221 {
2222     m_BuildJob             = job;
2223     m_BuildState           = bsNone;
2224     m_NextBuildState       = bsProjectPreBuild;
2225     m_pBuildingProject     = 0;
2226     m_pLastBuildingProject = 0;
2227     m_pLastBuildingTarget  = 0;
2228     m_BuildingTargetName   = target;
2229     m_CommandQueue.Clear();
2230 }
2231 
ResetBuildState()2232 void CompilerGCC::ResetBuildState()
2233 {
2234     if (m_pBuildingProject)
2235         m_pBuildingProject->SetCurrentlyCompilingTarget(0);
2236     else if (m_pProject)
2237         m_pProject->SetCurrentlyCompilingTarget(0);
2238 
2239     // reset state
2240     m_BuildJob = bjIdle;
2241     m_BuildState = bsNone;
2242     m_NextBuildState = bsNone;
2243     m_pBuildingProject = 0;
2244     m_BuildingTargetName.Clear();
2245 
2246     m_pLastBuildingProject = 0;
2247     m_pLastBuildingTarget = 0;
2248 
2249     m_CommandQueue.Clear();
2250 
2251     // Clear the Active Project's currently compiling target
2252     // NOTE (rickg22#1#): This way we can prevent Codeblocks from shutting down
2253     // when a project is being compiled.
2254     // NOTE (mandrav#1#): Make sure no open project is marked as compiling
2255     ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
2256     for (size_t i = 0; i < arr->GetCount(); ++i)
2257     {
2258         arr->Item(i)->SetCurrentlyCompilingTarget(0);
2259     }
2260 }
2261 
StateToString(BuildState bs)2262 inline wxString StateToString(BuildState bs)
2263 {
2264     switch (bs)
2265     {
2266         case bsNone:             return _T("bsNone");
2267         case bsProjectPreBuild:  return _T("bsProjectPreBuild");
2268         case bsTargetPreBuild:   return _T("bsTargetPreBuild");
2269         case bsTargetClean:      return _T("bsTargetClean");
2270         case bsTargetBuild:      return _T("bsTargetBuild");
2271         case bsTargetPostBuild:  return _T("bsTargetPostBuild");
2272         case bsTargetDone:       return _T("bsTargetDone");
2273         case bsProjectPostBuild: return _T("bsProjectPostBuild");
2274         case bsProjectDone:      return _T("bsProjectDone");
2275         default:                 break;
2276     }
2277     return _T("Huh!?!");
2278 }
2279 
GetNextStateBasedOnJob()2280 BuildState CompilerGCC::GetNextStateBasedOnJob()
2281 {
2282     bool clean = m_Clean;
2283     bool build = m_Build;
2284 
2285     switch (m_BuildState)
2286     {
2287         case bsProjectPreBuild:
2288         {
2289             if (clean && !build)
2290                 return bsTargetClean;
2291 
2292             return bsTargetPreBuild;
2293         }
2294 
2295         case bsTargetPreBuild:
2296         {
2297             if      (clean)
2298                 return bsTargetClean;
2299             else if (build)
2300                 return bsTargetBuild;
2301 
2302             return bsTargetPostBuild;
2303         }
2304 
2305         case bsTargetClean:
2306         {
2307             if (build)
2308                 return bsTargetBuild;
2309 
2310             return bsTargetDone;
2311         }
2312 
2313         case bsTargetBuild:
2314             return bsTargetPostBuild;
2315 
2316         case bsTargetPostBuild:
2317             return bsTargetDone;
2318 
2319         // advance target in the project
2320         case bsTargetDone:
2321         {
2322             // get next build job
2323             if (m_BuildJob != bjTarget)
2324             {
2325                 const BuildJobTarget& bj = PeekNextJob();
2326                 if (bj.project && bj.project == m_pBuildingProject)
2327                 {
2328                     // same project, switch target
2329                     m_BuildingTargetName = bj.targetName;
2330                     GetNextJob(); // remove job from queue, bj points to a destructed object
2331                     // switching targets
2332                     if (clean && !build)
2333                         return bsTargetClean;
2334 
2335                     return bsTargetPreBuild;
2336                 }
2337                 // switch project
2338                 // don't run postbuild step, if we only clean the project
2339                 if (build)
2340                     return bsProjectPostBuild;
2341 
2342                 return bsProjectDone;
2343             }
2344             m_pBuildingProject->SetCurrentlyCompilingTarget(0);
2345             break; // all done
2346         }
2347 
2348         case bsProjectPostBuild:
2349             return bsProjectDone;
2350 
2351         case bsProjectDone:
2352         {
2353             // switch to next project in workspace
2354             if (m_pBuildingProject)
2355                 m_pBuildingProject->SetCurrentlyCompilingTarget(0);
2356             m_NextBuildState = bsProjectPreBuild;
2357             // DoBuild runs ProjectPreBuild, next step has to be TargetClean or TargetPreBuild
2358             if (DoBuild(clean, build) >= 0)
2359             {
2360                 if (clean && !build)
2361                     return bsTargetClean;
2362 
2363                 return bsTargetPreBuild;
2364             }
2365             else
2366                 return bsNone;
2367         }
2368 
2369         case bsNone: // fall-through
2370         default:
2371             break;
2372     }
2373     return bsNone;
2374 }
2375 
BuildStateManagement()2376 void CompilerGCC::BuildStateManagement()
2377 {
2378 //    Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("BuildStateManagement")));
2379     if (IsProcessRunning())
2380         return;
2381 
2382     Manager::Yield();
2383     if (!m_pBuildingProject)
2384     {
2385         ResetBuildState();
2386         return;
2387     }
2388 
2389     ProjectBuildTarget* bt = m_pBuildingProject->GetBuildTarget(GetTargetIndexFromName(m_pBuildingProject, m_BuildingTargetName));
2390     if (!bt)
2391     {
2392         ResetBuildState();
2393         return;
2394     }
2395 
2396     if (m_pBuildingProject != m_pLastBuildingProject || bt != m_pLastBuildingTarget)
2397     {
2398         Manager::Get()->GetMacrosManager()->RecalcVars(m_pBuildingProject, Manager::Get()->GetEditorManager()->GetActiveEditor(), bt);
2399         if (bt)
2400             SwitchCompiler(bt->GetCompilerID());
2401 
2402         if (m_pBuildingProject != m_pLastBuildingProject)
2403         {
2404             m_pLastBuildingProject = m_pBuildingProject;
2405             wxSetWorkingDirectory(m_pBuildingProject->GetBasePath());
2406         }
2407         if (bt != m_pLastBuildingTarget)
2408             m_pLastBuildingTarget = bt;
2409     }
2410 
2411     m_pBuildingProject->SetCurrentlyCompilingTarget(bt);
2412     DirectCommands dc(this, CompilerFactory::GetCompiler(bt->GetCompilerID()), m_pBuildingProject, m_PageIndex);
2413     dc.m_doYield = true;
2414 
2415     m_BuildState = m_NextBuildState;
2416     wxArrayString cmds;
2417     switch (m_NextBuildState)
2418     {
2419         case bsProjectPreBuild:
2420         {
2421             // don't run project pre-build steps if we only clean it
2422             if (m_Build)
2423                 cmds = dc.GetPreBuildCommands(0);
2424             break;
2425         }
2426 
2427         case bsTargetPreBuild:
2428         {
2429             // check if it should build with "All"
2430             // run target pre-build steps
2431             cmds = dc.GetPreBuildCommands(bt);
2432             // Print Build banner here, else preBuild commands appear to belong to previous target
2433             PrintBanner(baBuild, m_pBuildingProject, bt);
2434             break;
2435         }
2436 
2437         case bsTargetClean:
2438         {
2439             PrintBanner(baClean, m_pBuildingProject, bt);
2440 
2441             bool result;
2442             if ( UseMake(m_pBuildingProject) )
2443                 result = DoCleanWithMake(bt);
2444             else
2445             {
2446                 wxArrayString clean = dc.GetCleanCommands(bt, true);
2447                 DoClean(clean);
2448                 result = true;
2449             }
2450 
2451             if (result)
2452             {
2453                 const wxString &message = F(_("Cleaned \"%s - %s\""), m_pBuildingProject->GetTitle().wx_str(),
2454                                             getBuildTargetName(bt).wx_str());
2455                 LogMessage(message, cltNormal);
2456             }
2457             else
2458             {
2459                 const wxString &message = F(_("Error cleaning \"%s - %s\""), m_pBuildingProject->GetTitle().wx_str(),
2460                                             getBuildTargetName(bt).wx_str());
2461                 LogMessage(COMPILER_ERROR_LOG + message, cltError);
2462             }
2463             break;
2464         }
2465 
2466         case bsTargetBuild:
2467         {
2468             // Build banner has already been printed at bsTargetPreBuild
2469             // run target build
2470             if ( UseMake(m_pBuildingProject) )
2471             {
2472                 wxArrayString output, error;
2473                 wxSetWorkingDirectory(m_pBuildingProject->GetExecutionDir());
2474 
2475                 const wxString &askCmd = GetMakeCommandFor(mcAskRebuildNeeded, m_pBuildingProject, bt);
2476 
2477                 Compiler* tgtCompiler = CompilerFactory::GetCompiler(bt->GetCompilerID());
2478 
2479                 bool runMake = false;
2480                 if (!askCmd.empty())
2481                 {
2482                     if (tgtCompiler && tgtCompiler->GetSwitches().logging == clogFull)
2483                         cmds.Add(COMPILER_SIMPLE_LOG + _("Checking if target is up-to-date: ") + askCmd);
2484 
2485                     runMake = (wxExecute(askCmd, output, error, wxEXEC_SYNC | wxEXEC_NODISABLE) != 0);
2486                 }
2487                 else
2488                 {
2489                     cmds.Add(COMPILER_SIMPLE_LOG +
2490                              _("The command that asks if a rebuild is needed is empty. Assuming rebuild is needed!"));
2491                     runMake = true;
2492                 }
2493 
2494                 if (runMake && tgtCompiler)
2495                 {
2496                     bool isEmpty = false;
2497                     switch (tgtCompiler->GetSwitches().logging)
2498                     {
2499                         case clogFull:
2500                         {
2501                             const wxString &cmd = GetMakeCommandFor(mcBuild, m_pBuildingProject, bt);
2502                             if (!cmd.empty())
2503                             {
2504                                 cmds.Add(COMPILER_SIMPLE_LOG + _("Running command: ") + cmd);
2505                                 cmds.Add(cmd);
2506                             }
2507                             else
2508                                 isEmpty = true;
2509                             break;
2510                         }
2511 
2512                         case clogSimple:
2513                             cmds.Add(COMPILER_SIMPLE_LOG + _("Using makefile: ") + m_pBuildingProject->GetMakefile());
2514                         case clogNone:
2515                         {
2516                             const wxString &cmd = GetMakeCommandFor(mcSilentBuild, m_pBuildingProject, bt);
2517                             if (!cmd.empty())
2518                                 cmds.Add(cmd);
2519                             else
2520                                 isEmpty = true;
2521                             break;
2522                         }
2523 
2524                         default:
2525                             break;
2526                     }
2527 
2528                     if (isEmpty)
2529                     {
2530                         cmds.Add(COMPILER_ERROR_LOG +
2531                                  _("Make command for 'Build/Project target' is empty. Nothing will be built!"));
2532                     }
2533 
2534                 }
2535             }
2536             else
2537                 cmds = dc.GetCompileCommands(bt);
2538 
2539             bool hasCommands = cmds.GetCount();
2540             m_RunTargetPostBuild = hasCommands;
2541             m_RunProjectPostBuild = hasCommands;
2542             if (!hasCommands)
2543                 LogMessage(_("Target is up to date."));
2544             break;
2545         }
2546 
2547         case bsTargetPostBuild:
2548         {
2549             // run target post-build steps
2550             if (m_RunTargetPostBuild || bt->GetAlwaysRunPostBuildSteps())
2551                 cmds = dc.GetPostBuildCommands(bt);
2552             // reset
2553             m_RunTargetPostBuild = false;
2554             break;
2555         }
2556 
2557         case bsProjectPostBuild:
2558         {
2559             // run project post-build steps
2560             if (m_RunProjectPostBuild || m_pBuildingProject->GetAlwaysRunPostBuildSteps())
2561                 cmds = dc.GetPostBuildCommands(0);
2562             // reset
2563             m_pLastBuildingTarget = 0;
2564             m_RunProjectPostBuild = false;
2565             break;
2566         }
2567 
2568         case bsProjectDone:
2569         {
2570             m_pLastBuildingProject = 0;
2571             break;
2572         }
2573 
2574         case bsTargetDone: // fall-through
2575         case bsNone:       // fall-through
2576         default:
2577             break;
2578     }
2579     m_NextBuildState = GetNextStateBasedOnJob();
2580     AddToCommandQueue(cmds);
2581     Manager::Yield();
2582 }
2583 
GetTargetIndexFromName(cbProject * prj,const wxString & name)2584 int CompilerGCC::GetTargetIndexFromName(cbProject* prj, const wxString& name)
2585 {
2586     if (!prj || name.IsEmpty())
2587         return -1;
2588     for (int i = 0; i < prj->GetBuildTargetsCount(); ++i)
2589     {
2590         ProjectBuildTarget* bt_search =  prj->GetBuildTarget(i);
2591         if (bt_search->GetTitle() == name)
2592             return i;
2593     }
2594     return -1;
2595 }
2596 
ExpandTargets(cbProject * project,const wxString & targetName,wxArrayString & result)2597 void CompilerGCC::ExpandTargets(cbProject* project, const wxString& targetName, wxArrayString& result)
2598 {
2599     result.Clear();
2600     if (project)
2601     {
2602         ProjectBuildTarget* bt =  project->GetBuildTarget(targetName);
2603         if (bt) // real target
2604             result.Add(targetName);
2605         else // virtual target
2606             result = project->GetExpandedVirtualBuildTargetGroup(targetName);
2607     }
2608 }
2609 
PreprocessJob(cbProject * project,const wxString & targetName)2610 void CompilerGCC::PreprocessJob(cbProject* project, const wxString& targetName)
2611 {
2612     wxArrayString tlist;
2613 
2614     // if not a workspace operation, clear any remaining (old) build jobs
2615     if (!m_IsWorkspaceOperation)
2616     {
2617         while (!m_BuildJobTargetsList.empty())
2618             m_BuildJobTargetsList.pop();
2619     }
2620 
2621     // calculate project/workspace dependencies
2622     wxArrayInt deps;
2623     if (!project)
2624         CalculateWorkspaceDependencies(deps);
2625     else
2626         CalculateProjectDependencies(project, deps);
2627 
2628     // loop all projects in the dependencies list
2629 //    Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("** Creating deps")));
2630     for (size_t i = 0; i < deps.GetCount(); ++i)
2631     {
2632         cbProject* prj = Manager::Get()->GetProjectManager()->GetProjects()->Item(deps[i]);
2633 
2634         if (!prj->SupportsCurrentPlatform())
2635         {
2636             wxString msg;
2637             msg.Printf(_T("\"%s\" does not support the current platform. Skipping..."),
2638                         prj->GetTitle().wx_str());
2639             Manager::Get()->GetLogManager()->LogWarning(msg, m_PageIndex);
2640             continue;
2641         }
2642 
2643         ExpandTargets(prj, targetName, tlist);
2644 
2645         if (tlist.GetCount() == 0)
2646             Manager::Get()->GetLogManager()->LogWarning(F(_T("Warning: No target named '%s' in project '%s'. Project will not be built..."), targetName.wx_str(), prj->GetTitle().wx_str()));
2647 
2648         // add all matching targets in the job list
2649         for (size_t x = 0; x < tlist.GetCount(); ++x)
2650         {
2651             ProjectBuildTarget* tgt = prj->GetBuildTarget(tlist[x]);
2652             CompilerValidResult result = CompilerValid(tgt);
2653             if (!result.isValid)
2654             {
2655                 PrintInvalidCompiler(tgt, result.compiler, _T("Skipping..."));
2656                 continue;
2657             }
2658             else if (!tgt->SupportsCurrentPlatform())
2659             {
2660                 wxString msg;
2661                 msg.Printf(_T("\"%s - %s\" does not support the current platform. Skipping..."),
2662                             prj->GetTitle().wx_str(), tlist[x].wx_str());
2663                 Manager::Get()->GetLogManager()->LogWarning(msg, m_PageIndex);
2664                 continue;
2665             }
2666             BuildJobTarget bjt;
2667             bjt.project = prj;
2668             bjt.targetName = tlist[x];
2669 
2670             m_BuildJobTargetsList.push(bjt);
2671 
2672 //            Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("Job: %s - %s"), prj->GetTitle().c_str(), prj->GetBuildTarget(tlist[x])->GetTitle().c_str()));
2673         }
2674     }
2675 
2676     // were there any jobs generated?
2677     if (m_BuildJobTargetsList.empty())
2678         NotifyJobDone(true);
2679 
2680 //    Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("** Done creating deps")));
2681 }
2682 
GetNextJob()2683 CompilerGCC::BuildJobTarget CompilerGCC::GetNextJob()
2684 {
2685     BuildJobTarget ret;
2686     if (m_BuildJobTargetsList.empty())
2687         return ret;
2688     ret = m_BuildJobTargetsList.front();
2689     m_BuildJobTargetsList.pop();
2690     return ret;
2691 }
2692 
PeekNextJob()2693 const CompilerGCC::BuildJobTarget& CompilerGCC::PeekNextJob()
2694 {
2695     static BuildJobTarget ret;
2696 
2697     if (m_BuildJobTargetsList.empty())
2698         return ret;
2699     return m_BuildJobTargetsList.front();
2700 }
2701 
DoBuild(bool clean,bool build)2702 int CompilerGCC::DoBuild(bool clean, bool build)
2703 {
2704     BuildJobTarget bj = GetNextJob();
2705 
2706     // no jobs list?
2707     if (!bj.project)
2708         return -2;
2709 
2710     // make sure all project files are saved
2711     if (    bj.project
2712         && (bj.project != m_pBuildingProject)
2713         && !bj.project->SaveAllFiles() )  // avoid saving when we only switch targets
2714     {
2715         Manager::Get()->GetLogManager()->Log(_("Could not save all files..."));
2716     }
2717 
2718     m_pBuildingProject = bj.project;
2719     m_BuildingTargetName = bj.targetName;
2720     ProjectBuildTarget* bt = bj.project->GetBuildTarget(bj.targetName);
2721 
2722     m_Clean = clean;
2723     m_Build = build;
2724 
2725     if (!bt || !CompilerValid(bt).isValid)
2726         return -2;
2727 
2728     BuildStateManagement();
2729 
2730     return 0;
2731 }
2732 
CalculateWorkspaceDependencies(wxArrayInt & deps)2733 void CompilerGCC::CalculateWorkspaceDependencies(wxArrayInt& deps)
2734 {
2735     deps.Clear();
2736     ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
2737     for (size_t i = 0; i < arr->GetCount(); ++i)
2738     {
2739         CalculateProjectDependencies(arr->Item(i), deps);
2740     }
2741 }
2742 
CalculateProjectDependencies(cbProject * prj,wxArrayInt & deps)2743 void CompilerGCC::CalculateProjectDependencies(cbProject* prj, wxArrayInt& deps)
2744 {
2745     int prjidx = Manager::Get()->GetProjectManager()->GetProjects()->Index(prj);
2746     const ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetDependenciesForProject(prj);
2747     if (!arr || !arr->GetCount())
2748     {
2749         // no dependencies; add the project in question and exit
2750         if (deps.Index(prjidx) == wxNOT_FOUND)
2751         {
2752 //            Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("Adding dependency: %s"), prj->GetTitle().c_str()));
2753             deps.Add(prjidx);
2754         }
2755         return;
2756     }
2757 
2758     for (size_t i = 0; i < arr->GetCount(); ++i)
2759     {
2760         cbProject* thisprj = arr->Item(i);
2761         if (!Manager::Get()->GetProjectManager()->CausesCircularDependency(prj, thisprj))
2762         {
2763             // recursively check dependencies
2764             CalculateProjectDependencies(thisprj, deps);
2765 
2766             // find out project's index in full (open) projects array
2767             ProjectsArray* parr = Manager::Get()->GetProjectManager()->GetProjects();
2768             int idx = parr->Index(thisprj);
2769             if (idx != wxNOT_FOUND)
2770             {
2771                 // avoid duplicates
2772                 if (deps.Index(idx) == wxNOT_FOUND)
2773                 {
2774 //                    Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("Adding dependency: %s"), thisprj->GetTitle().c_str()));
2775                     deps.Add(idx);
2776                 }
2777             }
2778         }
2779         else
2780             Manager::Get()->GetLogManager()->Log(F(_("Circular dependency detected between \"%s\" and \"%s\". Skipping..."), prj->GetTitle().wx_str(), thisprj->GetTitle().wx_str()), m_PageIndex, Logger::warning);
2781     }
2782 
2783     // always add the project in question
2784     if (deps.Index(prjidx) == wxNOT_FOUND)
2785     {
2786 //        Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("Adding dependency: %s"), prj->GetTitle().c_str()));
2787         deps.Add(prjidx);
2788     }
2789 }
2790 
DoBuild(const wxString & target,bool clean,bool build,bool clearLog)2791 int CompilerGCC::DoBuild(const wxString& target, bool clean, bool build, bool clearLog)
2792 {
2793     wxString realTarget = target;
2794     if (realTarget.IsEmpty())
2795         realTarget = GetTargetString();
2796 
2797     if (!StopRunningDebugger())
2798         return -1;
2799 
2800     if (!CheckProject())
2801     {
2802         // no active project
2803         if (Manager::Get()->GetEditorManager()->GetActiveEditor())
2804             return CompileFile(Manager::Get()->GetEditorManager()->GetActiveEditor()->GetFilename());
2805         return -1;
2806     }
2807 
2808     if (realTarget.IsEmpty())
2809         return -1;
2810 
2811     if (!m_IsWorkspaceOperation)
2812     {
2813         DoClearErrors();
2814         InitBuildLog(false);
2815         DoPrepareQueue(clearLog);
2816         if (clean)
2817             NotifyCleanProject(realTarget);
2818     }
2819 
2820     PreprocessJob(m_pProject, realTarget);
2821     if (m_BuildJobTargetsList.empty())
2822         return -1;
2823 
2824     InitBuildState(bjProject, realTarget);
2825     if (DoBuild(clean, build))
2826         return -2;
2827 
2828     return DoRunQueue();
2829 }
2830 
Build(const wxString & target)2831 int CompilerGCC::Build(const wxString& target)
2832 {
2833     m_LastBuildStep = true;
2834     return DoBuild(target, false, true);
2835 }
2836 
Build(ProjectBuildTarget * target)2837 int CompilerGCC::Build(ProjectBuildTarget* target)
2838 {
2839     return Build(target ? target->GetTitle() : _T(""));
2840 }
2841 
Rebuild(ProjectBuildTarget * target)2842 int CompilerGCC::Rebuild(ProjectBuildTarget* target)
2843 {
2844     return Rebuild(target ? target->GetTitle() : _T(""));
2845 }
2846 
Rebuild(const wxString & target)2847 int CompilerGCC::Rebuild(const wxString& target)
2848 {
2849     m_LastBuildStep = Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/rebuild_seperately"), false);
2850     if (m_LastBuildStep)
2851         return DoBuild(target, true, true);
2852 
2853     int result = DoBuild(target, true, false);
2854     m_LastBuildStep = true;
2855     return result + DoBuild(target, false, true, false);
2856 }
2857 
DoWorkspaceBuild(const wxString & target,bool clean,bool build,bool clearLog)2858 int CompilerGCC::DoWorkspaceBuild(const wxString& target, bool clean, bool build, bool clearLog)
2859 {
2860     wxString realTarget = target;
2861     if (realTarget.IsEmpty())
2862         realTarget = GetTargetString();
2863     if (realTarget.IsEmpty())
2864         return -1;
2865 
2866     if (!StopRunningDebugger())
2867         return -1;
2868 
2869     DoPrepareQueue(clearLog);
2870     if (clean)
2871         NotifyCleanWorkspace();
2872     m_IsWorkspaceOperation = true;
2873 
2874     InitBuildLog(true);
2875 
2876     // save files from all projects as they might require each other...
2877     ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
2878     if (arr)
2879     {
2880         for (size_t i = 0; i < arr->GetCount(); ++i)
2881         {
2882             cbProject* prj = arr->Item(i);
2883             if (prj && !prj->SaveAllFiles())
2884                 Manager::Get()->GetLogManager()->Log(F(_("Could not save all files of %s..."), prj->GetTitle().wx_str()), m_PageIndex);
2885         }
2886     }
2887 
2888     // create list of jobs to run (project->realTarget pairs)
2889     PreprocessJob(0, realTarget);
2890     if (m_BuildJobTargetsList.empty())
2891         return -1;
2892 
2893     InitBuildState(bjWorkspace, realTarget);
2894 
2895     DoBuild(clean,build);
2896     m_IsWorkspaceOperation = false;
2897 
2898     return DoRunQueue();
2899 }
2900 
BuildWorkspace(const wxString & target)2901 int CompilerGCC::BuildWorkspace(const wxString& target)
2902 {
2903     return DoWorkspaceBuild(target, false, true);
2904 }
2905 
RebuildWorkspace(const wxString & target)2906 int CompilerGCC::RebuildWorkspace(const wxString& target)
2907 {
2908     m_LastBuildStep = Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/rebuild_seperately"), false);
2909     if (m_LastBuildStep)
2910         return DoWorkspaceBuild(target, true, true);
2911 
2912     int result = DoWorkspaceBuild(target, true, false);
2913     m_LastBuildStep = true;
2914     return result + DoWorkspaceBuild(target, false, true, false);
2915 }
2916 
CleanWorkspace(const wxString & target)2917 int CompilerGCC::CleanWorkspace(const wxString& target)
2918 {
2919     return DoWorkspaceBuild(target, true, false);
2920 }
2921 
KillProcess()2922 int CompilerGCC::KillProcess()
2923 {
2924     ResetBuildState();
2925     m_RunAfterCompile = false;
2926     if (!IsProcessRunning())
2927         return 0;
2928     if (!m_CommandQueue.LastCommandWasRun())
2929         LogMessage(_("Aborting build..."), cltInfo, ltMessages);
2930     wxKillError ret = wxKILL_OK;
2931 
2932     m_CommandQueue.Clear();
2933 
2934     for (CompilerProcess &p : m_CompilerProcessList)
2935     {
2936         if (!p.pProcess)
2937             continue;
2938 
2939         #if defined(WIN32) && defined(ENABLE_SIGTERM)
2940             ::GenerateConsoleCtrlEvent(0, p.PID);
2941         #endif
2942 
2943         // Close input pipe
2944         p.pProcess->CloseOutput();
2945         ((PipedProcess*) p.pProcess)->ForfeitStreams();
2946 
2947         wxLogNull nullLog;
2948         ret = wxProcess::Kill(p.PID, wxSIGKILL, wxKILL_CHILDREN);
2949 
2950         if (!platform::windows)
2951         {
2952             if (ret != wxKILL_OK)
2953             {
2954                 // No need to tell the user about the errors - just keep him waiting.
2955                 Manager::Get()->GetLogManager()->Log(F(_("Aborting process %ld ..."), p.PID), m_PageIndex);
2956             }
2957             else switch (ret)
2958             {
2959                 case wxKILL_OK:
2960                     Manager::Get()->GetLogManager()->Log(_("Process aborted (killed)."), m_PageIndex);
2961 //                case wxKILL_ACCESS_DENIED: cbMessageBox(_("Access denied"));     break;
2962 //                case wxKILL_NO_PROCESS:    cbMessageBox(_("No process"));        break;
2963 //                case wxKILL_BAD_SIGNAL:    cbMessageBox(_("Bad signal"));        break;
2964 //                case wxKILL_ERROR:         cbMessageBox(_("Unspecified error")); break;
2965                 case wxKILL_ACCESS_DENIED: // fall-through
2966                 case wxKILL_NO_PROCESS:    // fall-through
2967                 case wxKILL_BAD_SIGNAL:    // fall-through
2968                 case wxKILL_ERROR:         // fall-through
2969                 default:                   break;
2970             }
2971         }
2972     }
2973 
2974     ProjectManager *projectManager = Manager::Get()->GetProjectManager();
2975     if (projectManager->GetIsRunning() == this)
2976         projectManager->SetIsRunning(NULL);
2977     return ret;
2978 }
2979 
IsRunning() const2980 bool CompilerGCC::IsRunning() const
2981 {
2982     return m_BuildJob != bjIdle || IsProcessRunning() || m_CommandQueue.GetCount();
2983 }
2984 
GetBuildTargetForFile(ProjectFile * pf)2985 ProjectBuildTarget* CompilerGCC::GetBuildTargetForFile(ProjectFile* pf)
2986 {
2987     if (!pf)
2988         return nullptr;
2989 
2990     if (!pf->buildTargets.GetCount())
2991     {
2992         cbMessageBox(_("That file isn't assigned to any target."),
2993                     _("Information"), wxICON_INFORMATION);
2994         return nullptr;
2995     }
2996     // If a virtual target is selected, ask for build target.
2997     if (m_RealTargetIndex == -1)
2998     {
2999         int idx = DoGUIAskForTarget();
3000         if (idx == -1)
3001             return nullptr;
3002         return m_pProject->GetBuildTarget(idx);
3003     }
3004 
3005     // Use currently selected non-virtual target.
3006     // If the file is not added to this target return nullptr.
3007     const wxString &targetName = m_Targets[m_TargetIndex];
3008     if (std::find(pf->buildTargets.begin(), pf->buildTargets.end(), targetName) == pf->buildTargets.end())
3009         return nullptr;
3010     return m_pProject->GetBuildTarget(targetName);
3011 }
3012 
CompileFile(const wxString & file)3013 int CompilerGCC::CompileFile(const wxString& file)
3014 {
3015     CheckProject();
3016     DoClearErrors();
3017     DoPrepareQueue(false);
3018 
3019     ProjectFile* pf = m_pProject ? m_pProject->GetFileByFilename(file, true, false) : 0;
3020     ProjectBuildTarget* bt = GetBuildTargetForFile(pf);
3021 
3022     PrintBanner(baBuildFile, m_pProject, bt);
3023 
3024     if ( !CompilerValid(bt).isValid )
3025         return -1;
3026     if (!pf) // compile single file not belonging to a project
3027         return CompileFileWithoutProject(file);
3028     if (!bt)
3029     {
3030         const wxString err(_("error: Cannot find target for file"));
3031         LogMessage(pf->relativeToCommonTopLevelPath + _(": ") + err, cltError);
3032         LogWarningOrError(cltError, m_pProject, pf->relativeToCommonTopLevelPath, wxEmptyString, err);
3033         return -2;
3034     }
3035     if (m_pProject)
3036         wxSetWorkingDirectory(m_pProject->GetBasePath());
3037     return CompileFileDefault(m_pProject, pf, bt); // compile file using default build system
3038 }
3039 
CompileFileWithoutProject(const wxString & file)3040 int CompilerGCC::CompileFileWithoutProject(const wxString& file)
3041 {
3042     // compile single file not belonging to a project
3043     Manager::Get()->GetEditorManager()->Save(file);
3044 
3045     // switch to the default compiler
3046     SwitchCompiler(CompilerFactory::GetDefaultCompilerID());
3047     Manager::Get()->GetMacrosManager()->Reset();
3048 
3049     Compiler* compiler = CompilerFactory::GetDefaultCompiler();
3050 
3051     // get compile commands for file (always linked as console-executable)
3052     DirectCommands dc(this, compiler, 0, m_PageIndex);
3053     wxArrayString compile = dc.GetCompileSingleFileCommand(file);
3054     AddToCommandQueue(compile);
3055 
3056     return DoRunQueue();
3057 }
3058 
CompileFileDefault(cbProject * project,ProjectFile * pf,ProjectBuildTarget * bt)3059 int CompilerGCC::CompileFileDefault(cbProject* project, ProjectFile* pf, ProjectBuildTarget* bt)
3060 {
3061     Compiler* compiler = CompilerFactory::GetCompiler(bt->GetCompilerID());
3062     if (!compiler)
3063     {
3064         const wxString &err = wxString::Format(_("error: Cannot build file for target '%s'. Compiler '%s' cannot be found!"),
3065                                                bt->GetTitle().wx_str(), bt->GetCompilerID().wx_str());
3066         LogMessage(pf->relativeToCommonTopLevelPath + _(": ") + err, cltError);
3067         LogWarningOrError(cltError, project, pf->relativeToCommonTopLevelPath, wxEmptyString, err);
3068         return -3;
3069     }
3070 
3071     DirectCommands dc(this, compiler, project, m_PageIndex);
3072     wxArrayString compile = dc.CompileFile(bt, pf);
3073     AddToCommandQueue(compile);
3074 
3075     return DoRunQueue();
3076 }
3077 
3078 // events
3079 
OnIdle(wxIdleEvent & event)3080 void CompilerGCC::OnIdle(wxIdleEvent& event)
3081 {
3082     if (IsProcessRunning())
3083     {
3084         for (const CompilerProcess &p : m_CompilerProcessList)
3085         {
3086             if (p.pProcess && (static_cast<PipedProcess*>(p.pProcess))->HasInput())
3087             {
3088                 event.RequestMore();
3089                 break;
3090             }
3091         }
3092     }
3093     else
3094         event.Skip();
3095 }
3096 
OnTimer(cb_unused wxTimerEvent & event)3097 void CompilerGCC::OnTimer(cb_unused wxTimerEvent& event)
3098 {
3099     wxWakeUpIdle();
3100 }
3101 
OnRun(cb_unused wxCommandEvent & event)3102 void CompilerGCC::OnRun(cb_unused wxCommandEvent& event)
3103 {
3104     if (Run() == 0)
3105         DoRunQueue();
3106 }
3107 
OnCompileAndRun(cb_unused wxCommandEvent & event)3108 void CompilerGCC::OnCompileAndRun(cb_unused wxCommandEvent& event)
3109 {
3110     ProjectBuildTarget* target = 0;
3111     m_RunAfterCompile = true;
3112     Build(target);
3113 }
3114 
OnCompile(wxCommandEvent & event)3115 void CompilerGCC::OnCompile(wxCommandEvent& event)
3116 {
3117     int bak = m_RealTargetIndex;
3118     if (event.GetId() == idMenuCompileFromProjectManager)
3119     {
3120         // we 're called from a menu in ProjectManager
3121         // let's check the selected project...
3122         DoSwitchProjectTemporarily();
3123     }
3124     ProjectBuildTarget* target = 0;
3125     Build(target);
3126     m_RealTargetIndex = bak;
3127 }
3128 
OnCompileFile(wxCommandEvent & event)3129 void CompilerGCC::OnCompileFile(wxCommandEvent& event)
3130 {
3131     // TODO (Rick#1#): Clean the file so it will always recompile
3132     wxFileName file;
3133     if (event.GetId() == idMenuCompileFileFromProjectManager)
3134         PrepareCompileFilePM(file);
3135     else
3136         PrepareCompileFile(file);
3137 
3138     StartCompileFile(file);
3139 }
3140 
OnCleanFile(wxCommandEvent & event)3141 void CompilerGCC::OnCleanFile(wxCommandEvent& event)
3142 {
3143     if (event.GetId() == idMenuCleanFileFromProjectManager)
3144     {
3145         FileTreeData* ftd = DoSwitchProjectTemporarily();
3146         ProjectFile* pf = ftd->GetProjectFile();
3147         if (!pf)
3148             return;
3149 
3150         ProjectBuildTarget* bt = GetBuildTargetForFile(pf);
3151         if (!bt)
3152             return;
3153 
3154         Compiler* compiler = CompilerFactory::GetCompiler(bt->GetCompilerID());
3155         if (!compiler)
3156             return;
3157 
3158         if ( !CheckProject() ) // ensures m_pProject is not NULL
3159           return;
3160 
3161         wxSetWorkingDirectory(m_pProject->GetBasePath());
3162 
3163         wxFileName fn(pf->GetObjName());
3164         wxString obj_name = (compiler->GetSwitches().UseFlatObjects) ? fn.GetFullName() : fn.GetFullPath();
3165         wxString obj_file = wxFileName(bt->GetObjectOutput() + wxFILE_SEP_PATH + obj_name).GetFullPath();
3166         Manager::Get()->GetMacrosManager()->ReplaceEnvVars(obj_file);
3167 
3168         if ( wxFileExists(obj_file) )
3169         {
3170             if ( wxRemoveFile(obj_file) )
3171                 Manager::Get()->GetLogManager()->DebugLog(F(_T("File has been removed: %s"), obj_file.wx_str()));
3172             else
3173                 Manager::Get()->GetLogManager()->DebugLog(F(_T("Removing file failed for: %s"), obj_file.wx_str()));
3174         }
3175         else
3176             Manager::Get()->GetLogManager()->DebugLog(F(_T("File to remove does not exist: %s"), obj_file.wx_str()));
3177     }
3178 }
3179 
OnRebuild(wxCommandEvent & event)3180 void CompilerGCC::OnRebuild(wxCommandEvent& event)
3181 {
3182     CheckProject();
3183     AnnoyingDialog dlg(_("Rebuild project"),
3184                         _("Rebuilding the project will cause the deletion of all "
3185                         "object files and building it from scratch.\nThis action "
3186                         "might take a while, especially if your project contains "
3187                         "more than a few files.\nAnother factor is your CPU "
3188                         "and the available system memory.\n\n"
3189                         "Are you sure you want to rebuild the entire project?"),
3190                     wxART_QUESTION);
3191     if (m_pProject && dlg.ShowModal() == AnnoyingDialog::rtNO)
3192         return;
3193 
3194     int bak = m_RealTargetIndex;
3195     if (event.GetId() == idMenuRebuildFromProjectManager)
3196     {
3197         // we 're called from a menu in ProjectManager
3198         // let's check the selected project...
3199         DoSwitchProjectTemporarily();
3200     }
3201     ProjectBuildTarget* target = 0;
3202     Rebuild(target);
3203     m_RealTargetIndex = bak;
3204 }
3205 
OnCompileAll(cb_unused wxCommandEvent & event)3206 void CompilerGCC::OnCompileAll(cb_unused wxCommandEvent& event)
3207 {
3208     BuildWorkspace();
3209 }
3210 
OnRebuildAll(cb_unused wxCommandEvent & event)3211 void CompilerGCC::OnRebuildAll(cb_unused wxCommandEvent& event)
3212 {
3213     AnnoyingDialog dlg(_("Rebuild workspace"),
3214                         _("Rebuilding ALL the open projects will cause the deletion of all "
3215                         "object files and building them from scratch.\nThis action "
3216                         "might take a while, especially if your projects contain "
3217                         "more than a few files.\nAnother factor is your CPU "
3218                         "and the available system memory.\n\n"
3219                         "Are you sure you want to rebuild ALL the projects?"),
3220                     wxART_QUESTION);
3221     if (dlg.ShowModal() == AnnoyingDialog::rtNO)
3222         return;
3223 
3224     RebuildWorkspace();
3225 }
3226 
OnCleanAll(cb_unused wxCommandEvent & event)3227 void CompilerGCC::OnCleanAll(cb_unused wxCommandEvent& event)
3228 {
3229     AnnoyingDialog dlg(_("Clean project"),
3230                         _("Cleaning ALL the open projects will cause the deletion "
3231                         "of all relevant object files.\nThis means that you will "
3232                         "have to build ALL your projects from scratch next time you "
3233                         "'ll want to build them.\nThat action "
3234                         "might take a while, especially if your projects contain "
3235                         "more than a few files.\nAnother factor is your CPU "
3236                         "and the available system memory.\n\n"
3237                         "Are you sure you want to proceed to cleaning?"),
3238                     wxART_QUESTION);
3239     if (dlg.ShowModal() == AnnoyingDialog::rtNO)
3240         return;
3241 
3242     CleanWorkspace();
3243 }
3244 
OnClean(wxCommandEvent & event)3245 void CompilerGCC::OnClean(wxCommandEvent& event)
3246 {
3247     CheckProject();
3248     AnnoyingDialog dlg(_("Clean project"),
3249                         _("Cleaning the target or project will cause the deletion "
3250                         "of all relevant object files.\nThis means that you will "
3251                         "have to build your project from scratch next time you "
3252                         "'ll want to build it.\nThat action "
3253                         "might take a while, especially if your project contains "
3254                         "more than a few files.\nAnother factor is your CPU "
3255                         "and the available system memory.\n\n"
3256                         "Are you sure you want to proceed to cleaning?"),
3257                     wxART_QUESTION);
3258     if (m_pProject && dlg.ShowModal() == AnnoyingDialog::rtNO)
3259         return;
3260 
3261     int bak = m_RealTargetIndex;
3262     if (event.GetId() == idMenuCleanFromProjectManager)
3263     {
3264         // we 're called from a menu in ProjectManager
3265         // let's check the selected project...
3266         DoSwitchProjectTemporarily();
3267     }
3268     ProjectBuildTarget* target = 0;
3269     Clean(target);
3270     m_RealTargetIndex = bak;
3271 }
3272 
OnProjectCompilerOptions(cb_unused wxCommandEvent & event)3273 void CompilerGCC::OnProjectCompilerOptions(cb_unused wxCommandEvent& event)
3274 {
3275     ProjectManager* manager = Manager::Get()->GetProjectManager();
3276     wxTreeCtrl* tree = manager->GetUI().GetTree();
3277     wxTreeItemId sel = manager->GetUI().GetTreeSelection();
3278     FileTreeData* ftd = sel.IsOk() ? (FileTreeData*)tree->GetItemData(sel) : 0;
3279     if (ftd)
3280     {
3281         // 'configure' selected target, if other than 'All'
3282         ProjectBuildTarget* target = 0;
3283         cbProject *currentProject = ftd->GetProject();
3284         if (currentProject == m_pProject)
3285         {
3286             if (m_RealTargetIndex != -1)
3287                 target = m_pProject->GetBuildTarget(m_RealTargetIndex);
3288         }
3289         else if (m_RealTargetIndex != -1 && m_pProject)
3290         {
3291             // If the users wants to change the options for the non-active project,
3292             // we try to find a target with the same name as the currently selected
3293             // target in the active project (if the target is not 'All').
3294             ProjectBuildTarget *activeTarget = m_pProject->GetBuildTarget(m_RealTargetIndex);
3295             if (activeTarget)
3296                 target = currentProject->GetBuildTarget(activeTarget->GetTitle());
3297         }
3298         Configure(currentProject, target, Manager::Get()->GetAppWindow());
3299     }
3300     else
3301     {
3302         if (cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject())
3303             Configure(prj, nullptr, Manager::Get()->GetAppWindow());
3304     }
3305 }
3306 
OnTargetCompilerOptions(cb_unused wxCommandEvent & event)3307 void CompilerGCC::OnTargetCompilerOptions(cb_unused wxCommandEvent& event)
3308 {
3309     int bak = m_RealTargetIndex;
3310     // we 're called from a menu in ProjectManager
3311     int idx = DoGUIAskForTarget();
3312     if (idx == -1)
3313         return;
3314     else
3315         m_RealTargetIndex = idx; // TODO: check
3316 
3317     // let's check the selected project...
3318     DoSwitchProjectTemporarily();
3319 
3320     ProjectBuildTarget* target = 0;
3321     m_RealTargetIndex = bak;
3322     Configure(m_pProject, target, Manager::Get()->GetAppWindow());
3323 }
3324 
OnKillProcess(cb_unused wxCommandEvent & event)3325 void CompilerGCC::OnKillProcess(cb_unused wxCommandEvent& event)
3326 {
3327     KillProcess();
3328 }
3329 
OnSelectTarget(wxCommandEvent & event)3330 void CompilerGCC::OnSelectTarget(wxCommandEvent& event)
3331 {
3332     int selection = -1;
3333     bool updateTools = false;
3334 
3335     if (event.GetId() == idToolTarget)
3336     {   // through the toolbar
3337         selection = event.GetSelection();
3338     }
3339     else if (event.GetId() == idMenuSelectTargetDialog)
3340     {
3341         // The select target menu is clicked, so we want to show a dialog with all targets
3342         IncrementalSelectArrayIterator iterator(m_Targets);
3343         IncrementalSelectDialog dlg(Manager::Get()->GetAppWindow(), &iterator, _("Select target..."),
3344                                     _("Choose target:"));
3345         if (dlg.ShowModal() == wxID_OK)
3346         {
3347             selection = dlg.GetSelection();
3348             updateTools = true;
3349         }
3350     }
3351     else
3352     {   // through Build->SelectTarget
3353         selection = event.GetId() - idMenuSelectTargetOther[0];
3354         updateTools = true;
3355     }
3356 
3357     if (selection >= 0)
3358     {
3359         Manager::Get()->GetProjectManager()->GetWorkspace()->SetPreferredTarget( GetTargetString(selection) );
3360         DoUpdateTargetMenu(selection);
3361         if (updateTools && m_pToolTarget)
3362             m_pToolTarget->SetSelection(selection);
3363     }
3364 }
3365 
OnNextError(cb_unused wxCommandEvent & event)3366 void CompilerGCC::OnNextError(cb_unused wxCommandEvent& event)
3367 {
3368     DoGotoNextError();
3369 }
3370 
OnPreviousError(cb_unused wxCommandEvent & event)3371 void CompilerGCC::OnPreviousError(cb_unused wxCommandEvent& event)
3372 {
3373     DoGotoPreviousError();
3374 }
3375 
OnClearErrors(cb_unused wxCommandEvent & event)3376 void CompilerGCC::OnClearErrors(cb_unused wxCommandEvent& event)
3377 {
3378     DoClearErrors();
3379 }
3380 
OnUpdateUI(wxUpdateUIEvent & event)3381 void CompilerGCC::OnUpdateUI(wxUpdateUIEvent& event)
3382 {
3383     cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
3384     cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
3385     wxMenuBar* mbar = Manager::Get()->GetAppFrame()->GetMenuBar();
3386     bool running = IsRunning();
3387 
3388     cbPlugin *runningPlugin = Manager::Get()->GetProjectManager()->GetIsRunning();
3389     bool otherRunning = runningPlugin && runningPlugin != this;
3390     if (mbar)
3391     {
3392         mbar->Enable(idMenuCompile,          !running && (prj || ed) && !otherRunning);
3393         mbar->Enable(idMenuBuildWorkspace,   !running && prj &&         !otherRunning);
3394 //        mbar->Enable(idMenuCompileFromProjectManager, !running && prj);
3395         mbar->Enable(idMenuCompileFile,      !running && ed &&          !otherRunning);
3396 //        mbar->Enable(idMenuCompileFileFromProjectManager, !running && prj);
3397 //        mbar->Enable(idMenuCleanFileFromProjectManager,   !running && prj);
3398         mbar->Enable(idMenuRebuild,          !running && prj &&         !otherRunning);
3399         mbar->Enable(idMenuRebuildWorkspace, !running && prj &&         !otherRunning);
3400 //        mbar->Enable(idMenuRebuildFromProjectManager, !running && prj);
3401         mbar->Enable(idMenuClean,            !running && prj &&         !otherRunning);
3402         mbar->Enable(idMenuCleanWorkspace,   !running && prj &&         !otherRunning);
3403 //        mbar->Enable(idMenuCleanFromProjectManager, !running && prj);
3404         mbar->Enable(idMenuCompileAndRun,    !running && (prj || ed) && !otherRunning);
3405         mbar->Enable(idMenuRun, !running && (prj || ed) &&              !otherRunning);
3406         mbar->Enable(idMenuKillProcess,       running);
3407         mbar->Enable(idMenuSelectTarget,     !running && prj &&         !otherRunning);
3408 
3409         mbar->Enable(idMenuNextError,     !running && (prj || ed) && m_Errors.HasNextError()     && !otherRunning);
3410         mbar->Enable(idMenuPreviousError, !running && (prj || ed) && m_Errors.HasPreviousError() && !otherRunning);
3411         mbar->Enable(idMenuClearErrors,                                                             !otherRunning);
3412 
3413         // Project menu
3414         mbar->Enable(idMenuProjectCompilerOptions, !running && prj && !otherRunning);
3415     }
3416 
3417     // enable/disable compiler toolbar buttons
3418     wxToolBar* tbar = m_pTbar;//Manager::Get()->GetAppWindow()->GetToolBar();
3419     if (tbar)
3420     {
3421         tbar->EnableTool(idMenuCompile,       !running && (prj || ed) && !otherRunning);
3422         tbar->EnableTool(idMenuRun,           !running && (prj || ed) && !otherRunning);
3423         tbar->EnableTool(idMenuCompileAndRun, !running && (prj || ed) && !otherRunning);
3424         tbar->EnableTool(idMenuRebuild,       !running && prj         && !otherRunning);
3425         tbar->EnableTool(idMenuKillProcess,    running && prj);
3426         tbar->EnableTool(idMenuSelectTargetDialog, !running && prj && !otherRunning);
3427 
3428         m_pToolTarget = XRCCTRL(*tbar, "idToolTarget", wxChoice);
3429         if (m_pToolTarget)
3430             m_pToolTarget->Enable(!running && prj && !otherRunning);
3431     }
3432 
3433     // allow other UpdateUI handlers to process this event
3434     // *very* important! don't forget it...
3435     event.Skip();
3436 }
3437 
OnProjectActivated(CodeBlocksEvent & event)3438 void CompilerGCC::OnProjectActivated(CodeBlocksEvent& event)
3439 {
3440     //NOTE: this function is also called on PROJECT_TARGETS_MODIFIED events
3441     //      to keep the combobox in sync
3442 
3443     cbProject* active = Manager::Get()->GetProjectManager()->GetActiveProject();
3444 //    DBGLOG(_T("Active: %s, Event: %s"),
3445 //            active ? active->GetTitle().c_str() : _T("<none>"),
3446 //            event.GetProject()->GetTitle().c_str());
3447     if (event.GetProject() == active)
3448         UpdateProjectTargets(event.GetProject());
3449 }
3450 
OnProjectLoaded(cb_unused CodeBlocksEvent & event)3451 void CompilerGCC::OnProjectLoaded(cb_unused CodeBlocksEvent& event)
3452 {
3453 }
3454 
OnProjectUnloaded(CodeBlocksEvent & event)3455 void CompilerGCC::OnProjectUnloaded(CodeBlocksEvent& event)
3456 {
3457     // just make sure we don't keep an invalid pointer around
3458     if (m_pProject == event.GetProject())
3459         m_pProject = 0;
3460 }
3461 
OnWorkspaceClosed(cb_unused CodeBlocksEvent & event)3462 void CompilerGCC::OnWorkspaceClosed(cb_unused CodeBlocksEvent& event)
3463 {
3464     ClearLog(false);
3465     DoClearErrors();
3466 }
3467 
OnCompileFileRequest(CodeBlocksEvent & event)3468 void CompilerGCC::OnCompileFileRequest(CodeBlocksEvent& event)
3469 {
3470     cbProject*  prj = event.GetProject();
3471     EditorBase* eb  = event.GetEditor();
3472     if (!prj || !eb)
3473     {
3474 //        Manager::Get()->GetLogManager()->DebugLog(_T("Compile file request skipped due to missing project or editor."));
3475         return;
3476     }
3477 
3478     const wxString& ed_filename = eb->GetFilename();
3479     wxFileName wx_filename;
3480     wx_filename.Assign(ed_filename);
3481     wx_filename.MakeRelativeTo( prj->GetBasePath() );
3482 
3483     wxString filepath = wx_filename.GetFullPath();
3484     if (filepath.IsEmpty())
3485     {
3486 //        Manager::Get()->GetLogManager()->DebugLog(_T("Compile file request skipped due to unresolvable file."));
3487         return;
3488     }
3489 
3490     ProjectFile* pf = prj->GetFileByFilename(UnixFilename(filepath), true, false);
3491     if (!pf || !pf->buildTargets.GetCount())
3492     {
3493 //            Manager::Get()->GetLogManager()->DebugLog(F(_T("Skipping incoming compile file request for '%s' (no project file or build targets)."), filepath.wx_str()));
3494         return;
3495     }
3496 
3497     ProjectBuildTarget* bt = 0;
3498     if (pf->buildTargets.GetCount() == 1)
3499         bt = prj->GetBuildTarget(pf->buildTargets[0]);
3500     else // belongs to two or more build targets, but maybe a valid virtual target is selected
3501         bt = prj->GetBuildTarget(m_RealTargetIndex); // pick the selected target
3502     if (!bt)
3503     {
3504 //        Manager::Get()->GetLogManager()->DebugLog(F(_T("Skipping incoming compile file request for '%s' (no build target)."), filepath.wx_str()));
3505         return;
3506     }
3507 
3508     Manager::Get()->GetLogManager()->DebugLog(F(_T("Executing incoming compile file request for '%s'."), filepath.wx_str()));
3509     CompileFileDefault(prj, pf, bt);
3510 }
3511 
OnGCCOutput(CodeBlocksEvent & event)3512 void CompilerGCC::OnGCCOutput(CodeBlocksEvent& event)
3513 {
3514     wxString msg = event.GetString();
3515     if (!msg.IsEmpty() &&
3516         !msg.Matches(_T("# ??*")))  // gcc 3.4 started displaying a line like this filter
3517                                     // when calculating dependencies. Until I check out
3518                                     // why this happens (and if there is a switch to
3519                                     // turn it off), I put this condition here to avoid
3520                                     // displaying it...
3521     {
3522         AddOutputLine(msg);
3523     }
3524 }
3525 
OnGCCError(CodeBlocksEvent & event)3526 void CompilerGCC::OnGCCError(CodeBlocksEvent& event)
3527 {
3528     wxString msg = event.GetString();
3529     if (!msg.IsEmpty())
3530         AddOutputLine(msg);
3531 }
3532 
OnGCCTerminated(CodeBlocksEvent & event)3533 void CompilerGCC::OnGCCTerminated(CodeBlocksEvent& event)
3534 {
3535     const int index = event.GetX();
3536     OnJobEnd(index, event.GetInt());
3537 }
3538 
AddOutputLine(const wxString & output,bool forceErrorColour)3539 void CompilerGCC::AddOutputLine(const wxString& output, bool forceErrorColour)
3540 {
3541     wxArrayString ignore_output = Manager::Get()->GetConfigManager(_T("compiler"))->ReadArrayString(_T("/ignore_output"));
3542     if (!ignore_output.IsEmpty())
3543     {
3544         for (size_t i = 0; i<ignore_output.GetCount(); ++i)
3545         {
3546             if (output.Find(ignore_output.Item(i)) != wxNOT_FOUND)
3547             {
3548                 Manager::Get()->GetLogManager()->DebugLog(F(_T("Ignoring compiler output: %s"), output.wx_str()));
3549                 return;
3550             }
3551         }
3552     }
3553 
3554     Compiler* compiler = CompilerFactory::GetCompiler(m_CompilerId);
3555     if (!compiler)
3556         return;
3557     CompilerLineType clt = compiler->CheckForWarningsAndErrors(output);
3558 
3559     // if max_errors reached, display a one-time message and do not log any more
3560     size_t maxErrors = Manager::Get()->GetConfigManager(_T("compiler"))->ReadInt(_T("/max_reported_errors"), 50);
3561     if (maxErrors > 0 && m_Errors.GetCount(cltError) == maxErrors)
3562     {
3563         // no matter what, everything goes into the build log
3564         LogMessage(output, clt, ltFile, forceErrorColour);
3565 
3566         if (!m_NotifiedMaxErrors)
3567         {
3568             m_NotifiedMaxErrors = true;
3569 
3570             // if we reached the max errors count, notify about it
3571             LogWarningOrError(cltNormal, 0, wxEmptyString, wxEmptyString, _("More errors follow but not being shown."));
3572             LogWarningOrError(cltNormal, 0, wxEmptyString, wxEmptyString, _("Edit the max errors limit in compiler options..."));
3573         }
3574         return;
3575     }
3576 
3577     // log to build messages if info/warning/error (aka != normal)
3578     if (clt != cltNormal)
3579     {
3580         // actually log message
3581         wxString last_error_filename = compiler->GetLastErrorFilename();
3582         if ( UseMake() )
3583         {
3584             wxFileName last_error_file(last_error_filename);
3585             if (!last_error_file.IsAbsolute())
3586             {
3587                 cbProject* project = m_pProject;
3588                 if (m_pLastBuildingTarget)
3589                     project = m_pLastBuildingTarget->GetParentProject();
3590                 else
3591                 {
3592                     AskForActiveProject();
3593                     project = m_pProject;
3594                 }
3595                 last_error_file = project->GetExecutionDir() + wxFileName::GetPathSeparator() + last_error_file.GetFullPath();
3596                 last_error_file.MakeRelativeTo(project->GetBasePath());
3597                 last_error_filename = last_error_file.GetFullPath();
3598             }
3599         }
3600         wxString msg = compiler->GetLastError();
3601         if (!compiler->WithMultiLineMsg() || (compiler->WithMultiLineMsg() && !msg.IsEmpty()))
3602             LogWarningOrError(clt, m_pBuildingProject, last_error_filename, compiler->GetLastErrorLine(), msg);
3603     }
3604 
3605     // add to log
3606     LogMessage(output, clt, ltAll, forceErrorColour);
3607 }
3608 
LogWarningOrError(CompilerLineType lt,cbProject * prj,const wxString & filename,const wxString & line,const wxString & msg)3609 void CompilerGCC::LogWarningOrError(CompilerLineType lt, cbProject* prj, const wxString& filename, const wxString& line, const wxString& msg)
3610 {
3611     // add build message
3612     wxArrayString errors;
3613     errors.Add(filename);
3614     errors.Add(line);
3615 
3616     wxString msgFix = msg; msgFix.Replace(wxT("\t"), wxT("    "));
3617     errors.Add(msgFix);
3618 
3619     Logger::level lv = (lt == cltError)   ? Logger::error
3620                      : (lt == cltWarning) ? Logger::warning : Logger::info;
3621 
3622     // when there are many lines (thousands) of output, auto fitting column width
3623     // is very expensive, so rate limit it to a maximum of 1 fit per 3 seconds
3624     static wxDateTime lastAutofitTime = wxDateTime((time_t)0);
3625     if ( lastAutofitTime < (wxDateTime::Now() - wxTimeSpan::Seconds(3)) )
3626     {
3627         lastAutofitTime = wxDateTime::Now();
3628         m_pListLog->Append(errors, lv, 2); // auto fit the 'Message' column
3629     }
3630     else
3631         m_pListLog->Append(errors, lv);
3632 
3633     // add to error keeping struct
3634     m_Errors.AddError(lt, prj, filename, line.IsEmpty() ? 0 : atoi(wxSafeConvertWX2MB(line.wc_str())), msg);
3635 }
3636 
LogMessage(const wxString & message,CompilerLineType lt,LogTarget log,bool forceErrorColour,bool isTitle,bool updateProgress)3637 void CompilerGCC::LogMessage(const wxString& message, CompilerLineType lt, LogTarget log, bool forceErrorColour, bool isTitle, bool updateProgress)
3638 {
3639     // Strip the
3640     wxString msgInput, msg;
3641     if (message.StartsWith(COMPILER_SIMPLE_LOG, &msg))
3642         msgInput = msg;
3643     else
3644         msgInput = message;
3645 
3646     if (msgInput.StartsWith(COMPILER_NOTE_ID_LOG, &msg))
3647         LogWarningOrError(lt, 0, wxEmptyString, wxEmptyString, msg);
3648     else if (msgInput.StartsWith(COMPILER_WARNING_ID_LOG, &msg))
3649     {
3650         if (lt != cltError)
3651             lt = cltWarning;
3652         LogWarningOrError(lt, nullptr, wxEmptyString, wxEmptyString, msg);
3653     }
3654     else if (msgInput.StartsWith(COMPILER_ERROR_ID_LOG, &msg))
3655     {
3656         if (lt != cltError)
3657             lt = cltWarning;
3658         LogWarningOrError(cltError, nullptr, wxEmptyString, wxEmptyString, msg);
3659     }
3660     else
3661         msg = msgInput;
3662 
3663     // log file
3664     if (log & ltFile)
3665     {
3666         if (forceErrorColour)
3667             m_BuildLogContents << _T("<font color=\"#a00000\">");
3668         else if (lt == cltError)
3669             m_BuildLogContents << _T("<font color=\"#ff0000\">");
3670         else if (lt == cltWarning)
3671             m_BuildLogContents << _T("<font color=\"#0000ff\">");
3672 
3673         if (isTitle)
3674             m_BuildLogContents << _T("<b>");
3675 
3676         // Replace the script quotation marks family by "
3677         // Using UTF codes to avoid "error: converting to execution character set: Illegal byte sequence"
3678         // -> for UTF codes see here: http://www.utf8-chartable.de/unicode-utf8-table.pl
3679         wxString sQuoted(msg);
3680         wxString sGA = wxString::FromUTF8("\x60");     // GRAVE ACCENT
3681         wxString sAA = wxString::FromUTF8("\xC2\xB4"); // ACUTE ACCENT
3682         sQuoted.Replace(sGA,     _T("\""),    true);
3683         sQuoted.Replace(sAA,     _T("\""),    true);
3684         // avoid conflicts with html-tags
3685         sQuoted.Replace(_T("&"), _T("&amp;"), true);
3686         sQuoted.Replace(_T("<"), _T("&lt;"),  true);
3687         sQuoted.Replace(_T(">"), _T("&gt;"),  true);
3688         m_BuildLogContents << sQuoted;
3689 
3690         if (isTitle)
3691             m_BuildLogContents << _T("</b>");
3692 
3693         if (lt == cltWarning || lt == cltError || forceErrorColour)
3694             m_BuildLogContents << _T("</font>");
3695 
3696         m_BuildLogContents << _T("<br />\n");
3697     }
3698 
3699     // log window
3700     if (log & ltMessages)
3701     {
3702         Logger::level lv = isTitle ? Logger::caption : Logger::info;
3703         if (forceErrorColour)
3704             lv = Logger::critical;
3705         else if (lt == cltError)
3706             lv = Logger::error;
3707         else if (lt == cltWarning)
3708             lv = Logger::warning;
3709 
3710         wxString progressMsg;
3711         if (updateProgress && m_CurrentProgress < m_MaxProgress)
3712         {
3713             ++m_CurrentProgress;
3714             if (m_LogBuildProgressPercentage)
3715             {
3716                 float p = (float)(m_CurrentProgress * 100.0f) / (float)m_MaxProgress;
3717                 progressMsg.Printf(_T("[%5.1f%%] "), p);
3718             }
3719             if (m_pLog->progress)
3720             {
3721                 m_pLog->progress->SetRange(m_MaxProgress);
3722                 m_pLog->progress->SetValue(m_CurrentProgress);
3723             }
3724         }
3725 
3726         Manager::Get()->GetLogManager()->Log(progressMsg + msg, m_PageIndex, lv);
3727         Manager::Get()->GetLogManager()->LogToStdOut(progressMsg + msg);
3728     }
3729 }
3730 
InitBuildLog(bool workspaceBuild)3731 void CompilerGCC::InitBuildLog(bool workspaceBuild)
3732 {
3733     wxString title;
3734     wxString basepath;
3735     wxString basename;
3736     if (!workspaceBuild && m_pProject)
3737     {
3738         title = m_pProject->GetTitle();
3739         basepath = m_pProject->GetBasePath();
3740         basename = wxFileName(m_pProject->GetFilename()).GetName();
3741     }
3742     else if (workspaceBuild)
3743     {
3744         cbWorkspace* wksp = Manager::Get()->GetProjectManager()->GetWorkspace();
3745         title = wksp->GetTitle();
3746         basepath = wxFileName(wksp->GetFilename()).GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
3747         basename = wxFileName(wksp->GetFilename()).GetName();
3748     }
3749 
3750     if (basename.IsEmpty())
3751         basename = _T("unnamed");
3752 
3753     // init HTML build log
3754     m_BuildStartTime = wxDateTime::Now();
3755     m_BuildLogTitle = title + _(" build log");
3756     m_BuildLogFilename = basepath;
3757     m_BuildLogFilename << basename << _T("_build_log.html");
3758     m_BuildLogContents.Clear();
3759     m_MaxProgress = 0;
3760     m_CurrentProgress = 0;
3761 }
3762 
SaveBuildLog()3763 void CompilerGCC::SaveBuildLog()
3764 {
3765     // if not enabled in the configuration, leave
3766     if (!Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/save_html_build_log"), false))
3767         return;
3768 
3769     if (m_BuildLogFilename.IsEmpty())
3770         return;
3771 
3772     // NOTE: if we want to add a CSS later on, we 'd have to edit:
3773     //       - this function and
3774     //       - LogMessage()
3775 
3776     wxFile f(m_BuildLogFilename, wxFile::write);
3777     if (!f.IsOpened())
3778         return;
3779 
3780     // first output the standard header blurb
3781     f.Write(_T("<html>\n"));
3782     f.Write(_T("<head>\n"));
3783     f.Write(_T("<title>") + m_BuildLogTitle + _T("</title>\n"));
3784     f.Write(_T("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"));
3785 
3786     f.Write(_T("</head>\n"));
3787     f.Write(_T("<body>\n"));
3788 
3789     // use fixed-width font
3790     f.Write(_T("<tt>\n"));
3791 
3792     // write the start-end time of the build
3793     f.Write(_("Build started on: "));
3794     f.Write(_T("<u>"));
3795     f.Write(m_BuildStartTime.Format(_T("%d-%m-%Y at %H:%M.%S")));
3796     f.Write(_T("</u><br />\n"));
3797     f.Write(_("Build ended on: "));
3798     f.Write(_T("<u>"));
3799     f.Write(wxDateTime::Now().Format(_T("%d-%m-%Y at %H:%M.%S")));
3800     f.Write(_T("</u><p />\n"));
3801 
3802     // output the main body
3803     f.Write(m_BuildLogContents);
3804 
3805     // done with fixed-width font
3806     f.Write(_T("</tt>\n"));
3807 
3808     // finally output the footer
3809     f.Write(_T("</body>\n"));
3810     f.Write(_T("</html>\n"));
3811 
3812     Manager::Get()->GetLogManager()->Log(_("Build log saved as: "), m_PageIndex);
3813     wxString tempBuildLogFilename = m_BuildLogFilename;
3814     tempBuildLogFilename.Replace(_("\\"), _("/"));
3815     wxURI tmpFilename = tempBuildLogFilename;
3816 
3817     Manager::Get()->GetLogManager()->Log(F(_T("file://%s"), tmpFilename.BuildURI().wx_str()), m_PageIndex, Logger::warning);
3818 }
3819 
OnJobEnd(size_t procIndex,int exitCode)3820 void CompilerGCC::OnJobEnd(size_t procIndex, int exitCode)
3821 {
3822 //    Manager::Get()->GetMessageManager()->Log(m_PageIndex, _T("JobDone: index=%u, exitCode=%d"), procIndex, exitCode));
3823     m_timerIdleWakeUp.Stop();
3824     CompilerProcess &process = m_CompilerProcessList.at(procIndex);
3825     process.PID = 0;
3826     process.pProcess = nullptr;
3827     wxString oFile = UnixFilename(process.OutputFile);
3828 
3829     if (m_LastExitCode == 0 || exitCode != 0) // prevent exit errors from being overwritten during multi-threaded build
3830         m_LastExitCode = exitCode;
3831     bool success(exitCode == 0);
3832     Compiler* compiler = CompilerFactory::GetCompiler(m_CompilerId);
3833     if (compiler)
3834         success = (exitCode >= 0) && (exitCode <= compiler->GetSwitches().statusSuccess);
3835 
3836     Manager::Get()->GetMacrosManager()->ReplaceMacros(oFile); // might contain macros!
3837     if (success && !oFile.IsEmpty())
3838     {
3839         wxLogNull silence; // In case opening the file fails
3840         wxFFile f(oFile.wx_str(), _T("r"));
3841         if (f.IsOpened())
3842         {
3843             size_t size = f.Length();
3844             f.Close();
3845 
3846             float displaySize;
3847             wxString units;
3848             if (size < 1024)
3849             {
3850                 displaySize = (float)size;
3851                 units = _("bytes");
3852             }
3853             else if (size < 1048576)
3854             {
3855                 displaySize = (float)size / 1024.0f;
3856                 units = _("KB");
3857             }
3858             else
3859             {
3860                 displaySize = (float)size / 1048576.0f;
3861                 units = _("MB");
3862             }
3863             wxString msg;
3864             msg.Printf(_("Output file is %s with size %.2f %s"), oFile.wx_str(), displaySize, units.wx_str());
3865             LogMessage(msg, cltNormal);
3866         }
3867     }
3868     if (success)
3869         m_LastExitCode = 0;
3870     if (m_CommandQueue.GetCount() != 0 && success)
3871         DoRunQueue(); // continue running commands while last exit code was 0.
3872     else
3873     {
3874         if (success)
3875         {
3876             if (IsProcessRunning())
3877             {
3878                 DoRunQueue();
3879                 return;
3880             }
3881 
3882             while (1)
3883             {
3884                 BuildStateManagement();
3885                 if (m_CommandQueue.GetCount())
3886                 {
3887                     DoRunQueue();
3888                     return;
3889                 }
3890                 if (m_BuildState == bsNone && m_NextBuildState == bsNone)
3891                     break;
3892             }
3893         }
3894         m_CommandQueue.Clear();
3895         ResetBuildState();
3896         // clear any remaining jobs (e.g. in case of build errors)
3897         while (!m_BuildJobTargetsList.empty())
3898             m_BuildJobTargetsList.pop();
3899 
3900         wxString msg = wxString::Format(_("Process terminated with status %d (%s)"), exitCode, GetMinSecStr().wx_str());
3901         if (m_LastExitCode == exitCode) // do not log extra if there is failure during multi-threaded build
3902             LogMessage(msg, success ? cltWarning : cltError, ltAll, !success);
3903         if (!m_CommandQueue.LastCommandWasRun())
3904         {
3905             if ( !IsProcessRunning() )
3906             {
3907                 msg = wxString::Format(_("%s (%s)"), GetErrWarnStr().wx_str(), GetMinSecStr().wx_str());
3908                 success = (m_LastExitCode >= 0) && (m_LastExitCode <= compiler->GetSwitches().statusSuccess);
3909                 LogMessage(msg, success ? cltWarning : cltError, ltAll, !success);
3910                 LogWarningOrError(cltNormal, 0, wxEmptyString, wxEmptyString,
3911                                   wxString::Format(_("=== Build %s: %s ==="),
3912                                                    wxString(m_LastExitCode == 0 ? _("finished") : _("failed")).wx_str(), msg.wx_str()));
3913                 m_pListLog->AutoFitColumns(2);
3914                 SaveBuildLog();
3915             }
3916             if (!Manager::IsBatchBuild() && m_pLog->progress)
3917                 m_pLog->progress->SetValue(0);
3918         }
3919         else
3920         {
3921             // last command was "Run"
3922             // force exit code to zero (0) or else debugger will think build failed if last run returned non-zero...
3923 // TODO (mandrav##): Maybe create and use GetLastRunExitCode()? Is it needed?
3924             m_LastExitCode = 0; // *might* not be needed any more, see NotifyJobDone()
3925         }
3926         Manager::Get()->GetLogManager()->Log(_T(" "), m_PageIndex); // blank line
3927 
3928         NotifyJobDone();
3929 
3930         if (!Manager::IsBatchBuild() && m_Errors.GetCount(cltError))
3931         {
3932             if (Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/auto_show_build_errors"), true))
3933             {
3934                 CodeBlocksLogEvent evtShow(cbEVT_SHOW_LOG_MANAGER);
3935                 Manager::Get()->ProcessEvent(evtShow);
3936             }
3937             CodeBlocksLogEvent evtSwitch(cbEVT_SWITCH_TO_LOG_WINDOW, m_pListLog);
3938             Manager::Get()->ProcessEvent(evtSwitch);
3939 
3940             if (Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/auto_focus_build_errors"), true))
3941                 m_pListLog->FocusError(m_Errors.GetFirstError());
3942         }
3943         else
3944         {
3945             if (m_RunAfterCompile)
3946             {
3947                 m_RunAfterCompile = false;
3948                 if (Run() == 0)
3949                     DoRunQueue();
3950             }
3951             else if (!Manager::IsBatchBuild())
3952             {
3953                 // switch to the "Build messages" window only if the active log window is "Build log"
3954                 CodeBlocksLogEvent evtGetActive(cbEVT_GET_ACTIVE_LOG_WINDOW);
3955                 Manager::Get()->ProcessEvent(evtGetActive);
3956                 if (evtGetActive.logger == m_pLog)
3957                 {
3958                     // don't close the message manager (if auto-hiding), if warnings are required to keep it open
3959                     if (m_Errors.GetCount(cltWarning) &&
3960                         Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/auto_show_build_warnings"), true))
3961                     {
3962                         CodeBlocksLogEvent evtShow(cbEVT_SHOW_LOG_MANAGER);
3963                         Manager::Get()->ProcessEvent(evtShow);
3964 
3965                         CodeBlocksLogEvent evtSwitch(cbEVT_SWITCH_TO_LOG_WINDOW, m_pListLog);
3966                         Manager::Get()->ProcessEvent(evtSwitch);
3967                     }
3968                     else // if message manager is auto-hiding, unlock it (i.e. close it)
3969                     {
3970                         CodeBlocksLogEvent evtShow(cbEVT_HIDE_LOG_MANAGER);
3971                         Manager::Get()->ProcessEvent(evtShow);
3972                     }
3973                 }
3974             }
3975         }
3976 
3977         m_RunAfterCompile = false;
3978 
3979         // no matter what happened with the build, return the focus to the active editor
3980         cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(Manager::Get()->GetEditorManager()->GetActiveEditor());
3981         if (ed)
3982             ed->GetControl()->SetFocus();
3983     }
3984 }
3985 
NotifyJobDone(bool showNothingToBeDone)3986 void CompilerGCC::NotifyJobDone(bool showNothingToBeDone)
3987 {
3988     if (!m_LastBuildStep)
3989         return;
3990 
3991     m_BuildJob = bjIdle;
3992     if (showNothingToBeDone && m_Errors.GetCount(cltError) == 0)
3993     {
3994         LogMessage(m_Clean ? _("Done.\n") : _("Nothing to be done (all items are up-to-date).\n"));
3995         // if message manager is auto-hiding, unlock it (i.e. close it)
3996         CodeBlocksLogEvent evtShow(cbEVT_HIDE_LOG_MANAGER);
3997         Manager::Get()->ProcessEvent(evtShow);
3998     }
3999 
4000     if (!IsProcessRunning())
4001     {
4002         ProjectManager *manager = Manager::Get()->GetProjectManager();
4003 
4004         // Check if this was a run operation and the application has been closed.
4005         // If this is the case we don't need to send cbEVT_COMPILER_FINISHED event.
4006         if (manager->GetIsRunning() == this)
4007             manager->SetIsRunning(NULL);
4008         else
4009         {
4010             CodeBlocksEvent evt(cbEVT_COMPILER_FINISHED, 0, m_pProject, 0, this);
4011             evt.SetInt(m_LastExitCode);
4012             Manager::Get()->ProcessEvent(evt);
4013         }
4014         m_LastExitCode = 0;
4015     }
4016 }
4017 
GetErrWarnStr()4018 wxString CompilerGCC::GetErrWarnStr()
4019 {
4020 #ifdef NO_TRANSLATION
4021     return wxString::Format(wxT("%u error%s, %u warning%s"),
4022                             m_Errors.GetCount(cltError),   wxString(m_Errors.GetCount(cltError)   == 1 ? wxT("") : wxT("s")).wx_str(),
4023                             m_Errors.GetCount(cltWarning), wxString(m_Errors.GetCount(cltWarning) == 1 ? wxT("") : wxT("s")).wx_str());
4024 #else
4025     return wxString::Format(_("%u error(s), %u warning(s)"),
4026                             m_Errors.GetCount(cltError), m_Errors.GetCount(cltWarning));
4027 #endif // NO_TRANSLATION
4028 }
4029 
GetMinSecStr()4030 wxString CompilerGCC::GetMinSecStr()
4031 {
4032     long int elapsed = (wxGetLocalTimeMillis() - m_StartTime).ToLong() / 1000;
4033     int mins =  elapsed / 60;
4034     int secs = (elapsed % 60);
4035 #ifdef NO_TRANSLATION
4036     return wxString::Format(wxT("%d minute%s, %d second%s"),
4037                             mins, wxString(mins == 1 ? wxT("") : wxT("s")).wx_str(),
4038                             secs, wxString(secs == 1 ? wxT("") : wxT("s")).wx_str());
4039 #else
4040     return wxString::Format(_("%d minute(s), %d second(s)"), mins, secs);
4041 #endif // NO_TRANSLATION
4042 }
4043