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"), ¤tPath) )
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("\""e&\""), 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("&"), true);
3686 sQuoted.Replace(_T("<"), _T("<"), true);
3687 sQuoted.Replace(_T(">"), _T(">"), 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