1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3 * http://www.gnu.org/licenses/lgpl-3.0.html
4 *
5 * $Revision: 11906 $
6 * $Id: projectloader.cpp 11906 2019-11-09 12:05:35Z fuscated $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/projectloader.cpp $
8 */
9
10 #include "sdk_precomp.h"
11
12 #ifndef CB_PRECOMP
13 #include <wx/confbase.h>
14 #include <wx/fileconf.h>
15 #include <wx/intl.h>
16 #include <wx/filename.h>
17 #include <wx/msgdlg.h>
18 #include <wx/stopwatch.h>
19 #include "manager.h"
20 #include "configmanager.h"
21 #include "projectmanager.h"
22 #include "logmanager.h"
23 #include "macrosmanager.h"
24 #include "cbproject.h"
25 #include "compilerfactory.h"
26 #include "globals.h"
27 #endif
28
29 #include <wx/dir.h>
30 #include <string>
31
32 #include <algorithm>
33 #include "filefilters.h"
34 #include "projectloader.h"
35 #include "projectloader_hooks.h"
36 #include "annoyingdialog.h"
37 #include "configmanager.h"
38 #include "tinywxuni.h"
39 #include "filegroupsandmasks.h"
40
ProjectLoader(cbProject * project)41 ProjectLoader::ProjectLoader(cbProject* project)
42 : m_pProject(project),
43 m_Upgraded(false),
44 m_OpenDirty(false),
45 m_1_4_to_1_5_deftarget(-1),
46 m_IsPre_1_6(false)
47 {
48 //ctor
49 }
50
~ProjectLoader()51 ProjectLoader::~ProjectLoader()
52 {
53 //dtor
54 }
55
Open(const wxString & filename)56 bool ProjectLoader::Open(const wxString& filename)
57 {
58 return Open(filename, nullptr);
59 }
60
Open(const wxString & filename,TiXmlElement ** ppExtensions)61 bool ProjectLoader::Open(const wxString& filename, TiXmlElement** ppExtensions)
62 {
63 LogManager* pMsg = Manager::Get()->GetLogManager();
64 if (!pMsg)
65 return false;
66
67 wxStopWatch sw;
68 pMsg->DebugLog(_T("Loading project file..."));
69 TiXmlDocument doc;
70 if (!TinyXML::LoadDocument(filename, &doc))
71 return false;
72
73 pMsg->DebugLog(_T("Parsing project file..."));
74 TiXmlElement* root;
75 TiXmlElement* proj;
76
77 root = doc.FirstChildElement("CodeBlocks_project_file");
78 if (!root)
79 {
80 // old tag
81 root = doc.FirstChildElement("Code::Blocks_project_file");
82 if (!root)
83 {
84 pMsg->DebugLog(_T("Not a valid Code::Blocks project file..."));
85 return false;
86 }
87 }
88 proj = root->FirstChildElement("Project");
89 if (!proj)
90 {
91 pMsg->DebugLog(_T("No 'Project' element in file..."));
92 return false;
93 }
94
95 m_IsPre_1_2 = false; // flag for some changed defaults in version 1.2
96 TiXmlElement* version = root->FirstChildElement("FileVersion");
97 // don't show messages if we 're running a batch build (i.e. no gui)
98 if (!Manager::IsBatchBuild() && version)
99 {
100 int major = PROJECT_FILE_VERSION_MAJOR;
101 int minor = PROJECT_FILE_VERSION_MINOR;
102 version->QueryIntAttribute("major", &major);
103 version->QueryIntAttribute("minor", &minor);
104
105 m_IsPre_1_6 = major < 1 || (major == 1 && minor < 6);
106
107 if (major < 1 ||
108 (major == 1 && minor < 2))
109 {
110 // pre-1.2
111 pMsg->DebugLog(F(_T("Project version is %d.%d. Defaults have changed since then..."), major, minor));
112 m_IsPre_1_2 = true;
113 }
114 else if (major >= PROJECT_FILE_VERSION_MAJOR && minor > PROJECT_FILE_VERSION_MINOR)
115 {
116 pMsg->DebugLog(F(_T("Project version is > %d.%d. Trying to load..."), PROJECT_FILE_VERSION_MAJOR, PROJECT_FILE_VERSION_MINOR));
117 AnnoyingDialog dlg(_("Project file format is newer/unknown"),
118 _("This project file was saved with a newer version of Code::Blocks.\n"
119 "Will try to load, but you should make sure all the settings were loaded correctly..."),
120 wxART_WARNING,
121 AnnoyingDialog::OK);
122 dlg.ShowModal();
123 }
124 else
125 {
126 // use one message for all changes
127 wxString msg;
128 wxString warn_msg;
129
130 // 1.5 -> 1.6: values matching defaults are not written to <Unit> sections
131 if (major == 1 && minor == 5)
132 {
133 msg << _("1.5 to 1.6:\n");
134 msg << _(" * only saves values that differ from defaults (i.e. project files are smaller now).\n");
135 msg << _(" * added object names generation mode setting (normal/extended).\n");
136 msg << _(" * added project notes.\n");
137 msg << _("\n");
138
139 warn_msg << _("* Project file updated to version 1.6:\n");
140 warn_msg << _(" When a project file is saved as version 1.6, it will NO LONGER be read correctly\n");
141 warn_msg << _(" by earlier Code::Blocks versions!\n");
142 warn_msg << _(" So, if you plan on using this project with an earlier Code::Blocks version, you\n");
143 warn_msg << _(" should probably NOT save this project as version 1.6...\n");
144 warn_msg << _("\n");
145 }
146
147 // 1.4 -> 1.5: updated custom build command per-project file
148 if (major == 1 && minor == 4)
149 {
150 msg << _("1.4 to 1.5:\n");
151 msg << _(" * added virtual build targets.\n");
152 msg << _("\n");
153 }
154
155 // 1.3 -> 1.4: updated custom build command per-project file
156 if (major == 1 && minor == 3)
157 {
158 msg << _("1.3 to 1.4:\n");
159 msg << _(" * changed the way custom file build commands are stored (no auto-conversion).\n");
160 msg << _("\n");
161 }
162
163 if (!msg.IsEmpty())
164 {
165 m_Upgraded = true;
166 msg.Prepend(wxString::Format(_("Project file format is older (%d.%d) than the current format (%d.%d).\n"
167 "The file will automatically be upgraded on save.\n"
168 "But please read the following list of changes, as some of them "
169 "might not automatically convert existing (old) settings.\n"
170 "If you don't understand what a change means, you probably don't "
171 "use that feature so you don't have to worry about it.\n\n"
172 "List of changes:\n"),
173 major,
174 minor,
175 PROJECT_FILE_VERSION_MAJOR,
176 PROJECT_FILE_VERSION_MINOR));
177 AnnoyingDialog dlg(_("Project file format changed"),
178 msg,
179 wxART_INFORMATION,
180 AnnoyingDialog::OK);
181 dlg.ShowModal();
182 }
183
184 if (!warn_msg.IsEmpty())
185 {
186 warn_msg.Prepend(_("!!! WARNING !!!\n\n"));
187 AnnoyingDialog dlg(_("Project file upgrade warning"),
188 warn_msg,
189 wxART_WARNING,
190 AnnoyingDialog::OK);
191 dlg.ShowModal();
192 }
193 }
194 }
195
196 DoProjectOptions(proj);
197 DoBuild(proj);
198 DoCompilerOptions(proj);
199 DoResourceCompilerOptions(proj);
200 DoLinkerOptions(proj);
201 DoIncludesOptions(proj);
202 DoLibsOptions(proj);
203 DoExtraCommands(proj);
204 DoUnits(proj);
205
206 // if targets still use the "build with all" flag,
207 // it's time for conversion
208 if (!m_pProject->HasVirtualBuildTarget(_T("All")))
209 {
210 wxArrayString all;
211 for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
212 {
213 ProjectBuildTarget* bt = m_pProject->GetBuildTarget(i);
214 if (bt && bt->GetIncludeInTargetAll())
215 all.Add(bt->GetTitle());
216 }
217 if (all.GetCount())
218 {
219 m_pProject->DefineVirtualBuildTarget(_T("All"), all);
220 m_Upgraded = true;
221 }
222 }
223
224 // convert old deftarget int to string
225 if (m_1_4_to_1_5_deftarget != -1)
226 {
227 ProjectBuildTarget* bt = m_pProject->GetBuildTarget(m_1_4_to_1_5_deftarget);
228 if (bt)
229 m_pProject->SetDefaultExecuteTarget(bt->GetTitle());
230 }
231
232 if (ppExtensions)
233 *ppExtensions = nullptr;
234
235 // as a last step, run all hooked callbacks
236 TiXmlElement* node = proj->FirstChildElement("Extensions");
237 if (node)
238 {
239 if (ppExtensions)
240 *ppExtensions = new TiXmlElement(*node);
241 ProjectLoaderHooks::CallHooks(m_pProject, node, true);
242 }
243
244 if (!version)
245 {
246 // pre 1.1 version
247 ConvertVersion_Pre_1_1();
248 // format changed also:
249 // removed <IncludeDirs> and <LibDirs> elements and added them as child elements
250 // in <Compiler> and <Linker> elements respectively
251 // so set m_Upgraded to true, irrespectively of libs detection...
252 m_Upgraded = true;
253 }
254 else
255 {
256 // do something important based on version
257 // wxString major = version->Attribute("major");
258 // wxString minor = version->Attribute("minor");
259 }
260
261 pMsg->DebugLog(wxString(_T("Done loading project in ")) << wxString::Format(_T("%d"), (int) sw.Time()) << _T("ms"));
262 return true;
263 }
264
ConvertVersion_Pre_1_1()265 void ProjectLoader::ConvertVersion_Pre_1_1()
266 {
267 // ask to detect linker libraries and move them to the new
268 // CompileOptionsBase linker libs container
269 wxString msg;
270 msg.Printf(_("Project \"%s\" was saved with an earlier version of Code::Blocks.\n"
271 "In the current version, link libraries are treated separately from linker options.\n"
272 "Do you want to auto-detect the libraries \"%s\" is using and configure it accordingly?"),
273 m_pProject->GetTitle().c_str(),
274 m_pProject->GetTitle().c_str());
275 if (cbMessageBox(msg, _("Question"), wxICON_QUESTION | wxYES_NO) == wxID_YES)
276 {
277 // project first
278 ConvertLibraries(m_pProject);
279
280 for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
281 {
282 ConvertLibraries(m_pProject->GetBuildTarget(i));
283 m_Upgraded = true;
284 }
285 }
286 }
287
ConvertLibraries(CompileTargetBase * object)288 void ProjectLoader::ConvertLibraries(CompileTargetBase* object)
289 {
290 wxArrayString linkerOpts = object->GetLinkerOptions();
291 wxArrayString linkLibs = object->GetLinkLibs();
292
293 wxString compilerId = object->GetCompilerID();
294 Compiler* compiler = CompilerFactory::GetCompiler(compilerId);
295 if (!compiler)
296 return;
297 wxString linkLib = compiler->GetSwitches().linkLibs;
298 wxString libExt = compiler->GetSwitches().libExtension;
299 size_t libExtLen = libExt.Length();
300
301 size_t i = 0;
302 while (i < linkerOpts.GetCount())
303 {
304 wxString opt = linkerOpts[i];
305 if (!linkLib.IsEmpty() && opt.StartsWith(linkLib))
306 {
307 opt.Remove(0, 2);
308 wxString ext = compiler->GetSwitches().libExtension;
309 if (!ext.IsEmpty())
310 ext = _T(".") + ext;
311 linkLibs.Add(compiler->GetSwitches().libPrefix + opt + ext);
312 linkerOpts.RemoveAt(i, 1);
313 }
314 else if (opt.Length() > libExtLen && opt.Right(libExtLen) == libExt)
315 {
316 linkLibs.Add(opt);
317 linkerOpts.RemoveAt(i, 1);
318 }
319 else
320 ++i;
321 }
322
323 object->SetLinkerOptions(linkerOpts);
324 object->SetLinkLibs(linkLibs);
325 }
326
DoMakeCommands(TiXmlElement * parentNode,CompileTargetBase * target)327 void ProjectLoader::DoMakeCommands(TiXmlElement* parentNode, CompileTargetBase* target)
328 {
329 if (!parentNode)
330 return; // no options
331
332 TiXmlElement* node;
333
334 node = parentNode->FirstChildElement("Build");
335 if (node && node->Attribute("command"))
336 target->SetMakeCommandFor(mcBuild, cbC2U(node->Attribute("command")));
337
338 node = parentNode->FirstChildElement("CompileFile");
339 if (node && node->Attribute("command"))
340 target->SetMakeCommandFor(mcCompileFile, cbC2U(node->Attribute("command")));
341
342 node = parentNode->FirstChildElement("Clean");
343 if (node && node->Attribute("command"))
344 target->SetMakeCommandFor(mcClean, cbC2U(node->Attribute("command")));
345
346 node = parentNode->FirstChildElement("DistClean");
347 if (node && node->Attribute("command"))
348 target->SetMakeCommandFor(mcDistClean, cbC2U(node->Attribute("command")));
349
350 node = parentNode->FirstChildElement("AskRebuildNeeded");
351 if (node && node->Attribute("command"))
352 target->SetMakeCommandFor(mcAskRebuildNeeded, cbC2U(node->Attribute("command")));
353
354 node = parentNode->FirstChildElement("SilentBuild");
355 if (node && node->Attribute("command"))
356 target->SetMakeCommandFor(mcSilentBuild, cbC2U(node->Attribute("command")));
357 }
358
DoVirtualTargets(TiXmlElement * parentNode)359 void ProjectLoader::DoVirtualTargets(TiXmlElement* parentNode)
360 {
361 if (!parentNode)
362 return;
363
364 TiXmlElement* node = parentNode->FirstChildElement("Add");
365 if (!node)
366 return; // no virtual targets
367
368 while (node)
369 {
370 if (node->Attribute("alias") && node->Attribute("targets"))
371 {
372 wxString alias = cbC2U(node->Attribute("alias"));
373 wxString targets = cbC2U(node->Attribute("targets"));
374 wxArrayString arr = GetArrayFromString(targets, _T(";"), true);
375
376 m_pProject->DefineVirtualBuildTarget(alias, arr);
377 }
378
379 node = node->NextSiblingElement("Add");
380 }
381 }
382
DoProjectOptions(TiXmlElement * parentNode)383 void ProjectLoader::DoProjectOptions(TiXmlElement* parentNode)
384 {
385 TiXmlElement* node = parentNode->FirstChildElement("Option");
386 if (!node)
387 return; // no options
388
389 wxString title;
390 wxString makefile;
391 bool makefile_custom = false;
392 wxString execution_dir;
393 wxString defaultTarget;
394 wxString compilerId = _T("gcc");
395 bool extendedObjectNames = false;
396 wxArrayString vfolders;
397 int platformsFinal = spAll;
398 PCHMode pch_mode = m_IsPre_1_2 ? pchSourceDir : pchObjectDir;
399 bool showNotes = false;
400 bool checkFiles = true;
401 wxString notes;
402
403 // loop through all options
404 while (node)
405 {
406 if (node->Attribute("title"))
407 {
408 title = cbC2U(node->Attribute("title"));
409 if (title.Trim().IsEmpty())
410 title = _T("untitled");
411 }
412
413 else if (node->Attribute("platforms"))
414 platformsFinal = GetPlatformsFromString(cbC2U(node->Attribute("platforms")));
415
416 else if (node->Attribute("makefile")) // there is only one attribute per option, so "else" is a safe optimisation
417 makefile = UnixFilename(cbC2U(node->Attribute("makefile")));
418
419 else if (node->Attribute("makefile_is_custom"))
420 makefile_custom = strncmp(node->Attribute("makefile_is_custom"), "1", 1) == 0;
421
422 else if (node->Attribute("execution_dir"))
423 execution_dir = UnixFilename(cbC2U(node->Attribute("execution_dir")));
424
425 // old default_target (int) node
426 else if (node->QueryIntAttribute("default_target", &m_1_4_to_1_5_deftarget) == TIXML_SUCCESS)
427 {
428 // we read the value
429 }
430
431 else if (node->Attribute("default_target"))
432 defaultTarget = cbC2U(node->Attribute("default_target"));
433
434 else if (node->Attribute("compiler"))
435 compilerId = GetValidCompilerID(cbC2U(node->Attribute("compiler")), _T("the project"));
436
437 else if (node->Attribute("extended_obj_names"))
438 extendedObjectNames = strncmp(node->Attribute("extended_obj_names"), "1", 1) == 0;
439
440 else if (node->Attribute("pch_mode"))
441 pch_mode = (PCHMode)atoi(node->Attribute("pch_mode"));
442
443 else if (node->Attribute("virtualFolders"))
444 vfolders = GetArrayFromString(cbC2U(node->Attribute("virtualFolders")), _T(";"));
445
446 else if (node->Attribute("show_notes"))
447 {
448 TiXmlHandle parentHandle(node);
449 TiXmlText* t = (TiXmlText *) parentHandle.FirstChild("notes").FirstChild().Node();
450 if (t)
451 notes = cbC2U(t->Value());
452 showNotes = !notes.IsEmpty() && strncmp(node->Attribute("show_notes"), "1", 1) == 0;
453 }
454 else if (node->Attribute("check_files"))
455 checkFiles = strncmp(node->Attribute("check_files"), "0", 1) != 0;
456
457 node = node->NextSiblingElement("Option");
458 }
459
460 m_pProject->SetTitle(title);
461 m_pProject->SetPlatforms(platformsFinal);
462 m_pProject->SetMakefile(makefile);
463 m_pProject->SetMakefileCustom(makefile_custom);
464 m_pProject->SetMakefileExecutionDir(execution_dir);
465 m_pProject->SetDefaultExecuteTarget(defaultTarget);
466 m_pProject->SetCompilerID(compilerId);
467 m_pProject->SetExtendedObjectNamesGeneration(extendedObjectNames);
468 m_pProject->SetModeForPCH(pch_mode);
469 m_pProject->SetVirtualFolders(vfolders);
470 m_pProject->SetNotes(notes);
471 m_pProject->SetShowNotesOnLoad(showNotes);
472 m_pProject->SetCheckForExternallyModifiedFiles(checkFiles);
473
474 DoMakeCommands(parentNode->FirstChildElement("MakeCommands"), m_pProject);
475 DoVirtualTargets(parentNode->FirstChildElement("VirtualTargets"));
476 }
477
DoBuild(TiXmlElement * parentNode)478 void ProjectLoader::DoBuild(TiXmlElement* parentNode)
479 {
480 TiXmlElement* node = parentNode->FirstChildElement("Build");
481 while (node)
482 {
483 TiXmlElement* opt = node->FirstChildElement("Script");
484 while (opt)
485 {
486 if (opt->Attribute("file"))
487 m_pProject->AddBuildScript(cbC2U(opt->Attribute("file")));
488
489 opt = opt->NextSiblingElement("Script");
490 }
491
492 DoBuildTarget(node);
493 DoEnvironment(node, m_pProject);
494 node = node->NextSiblingElement("Build");
495 }
496 }
497
DoBuildTarget(TiXmlElement * parentNode)498 void ProjectLoader::DoBuildTarget(TiXmlElement* parentNode)
499 {
500 TiXmlElement* node = parentNode->FirstChildElement("Target");
501 if (!node)
502 return; // no options
503
504 while (node)
505 {
506 ProjectBuildTarget* target = nullptr;
507 wxString title = cbC2U(node->Attribute("title"));
508 if (!title.IsEmpty())
509 target = m_pProject->AddBuildTarget(title);
510
511 if (target)
512 {
513 Manager::Get()->GetLogManager()->DebugLog(_T("Loading target ") + title);
514 DoBuildTargetOptions(node, target);
515 DoCompilerOptions(node, target);
516 DoResourceCompilerOptions(node, target);
517 DoLinkerOptions(node, target);
518 DoIncludesOptions(node, target);
519 DoLibsOptions(node, target);
520 DoExtraCommands(node, target);
521 DoEnvironment(node, target);
522 }
523
524 node = node->NextSiblingElement("Target");
525 }
526 }
527
DoBuildTargetOptions(TiXmlElement * parentNode,ProjectBuildTarget * target)528 void ProjectLoader::DoBuildTargetOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
529 {
530 TiXmlElement* node = parentNode->FirstChildElement("Option");
531 if (!node)
532 return; // no options
533
534 bool use_console_runner = true;
535 wxString output;
536 wxString imp_lib;
537 wxString def_file;
538 wxString working_dir;
539 wxString obj_output;
540 wxString deps_output;
541 wxString deps;
542 wxString added;
543 int type = -1;
544 int platformsFinal = spAll;
545 wxString compilerId = m_pProject->GetCompilerID();
546 wxString parameters;
547 wxString hostApplication;
548 bool runHostApplicationInTerminal = false;
549 bool includeInTargetAll = m_IsPre_1_2 ? true : false;
550 bool createStaticLib = false;
551 bool createDefFile = false;
552 int projectCompilerOptionsRelation = 3;
553 int projectLinkerOptionsRelation = 3;
554 int projectIncludeDirsRelation = 3;
555 int projectLibDirsRelation = 3;
556 int projectResIncludeDirsRelation = 3;
557 TargetFilenameGenerationPolicy prefixPolicy = tgfpNone; // tgfpNone for compat. with older projects
558 TargetFilenameGenerationPolicy extensionPolicy = tgfpNone;
559
560 while (node)
561 {
562 if (node->Attribute("platforms"))
563 platformsFinal = GetPlatformsFromString(cbC2U(node->Attribute("platforms")));
564
565 if (node->Attribute("use_console_runner"))
566 use_console_runner = strncmp(node->Attribute("use_console_runner"), "0", 1) != 0;
567
568 if (node->Attribute("output"))
569 output = UnixFilename(cbC2U(node->Attribute("output")));
570
571 if (node->Attribute("imp_lib"))
572 imp_lib = UnixFilename(cbC2U(node->Attribute("imp_lib")));
573
574 if (node->Attribute("def_file"))
575 def_file = UnixFilename(cbC2U(node->Attribute("def_file")));
576
577 if (node->Attribute("prefix_auto"))
578 prefixPolicy = atoi(node->Attribute("prefix_auto")) == 1 ? tgfpPlatformDefault : tgfpNone;
579
580 if (node->Attribute("extension_auto"))
581 extensionPolicy = atoi(node->Attribute("extension_auto")) == 1 ? tgfpPlatformDefault : tgfpNone;
582
583 if (node->Attribute("working_dir"))
584 working_dir = UnixFilename(cbC2U(node->Attribute("working_dir")));
585
586 if (node->Attribute("object_output"))
587 obj_output = UnixFilename(cbC2U(node->Attribute("object_output")));
588
589 if (node->Attribute("deps_output"))
590 deps_output = UnixFilename(cbC2U(node->Attribute("deps_output")));
591
592 if (node->Attribute("external_deps"))
593 deps = UnixFilename(cbC2U(node->Attribute("external_deps")));
594
595 if (node->Attribute("additional_output"))
596 added = UnixFilename(cbC2U(node->Attribute("additional_output")));
597
598 if (node->Attribute("type"))
599 type = atoi(node->Attribute("type"));
600
601 if (node->Attribute("compiler"))
602 compilerId = GetValidCompilerID(cbC2U(node->Attribute("compiler")), target->GetTitle());
603
604 if (node->Attribute("parameters"))
605 parameters = cbC2U(node->Attribute("parameters"));
606
607 if (node->Attribute("host_application"))
608 hostApplication = UnixFilename(cbC2U(node->Attribute("host_application")));
609
610 if (node->Attribute("run_host_application_in_terminal"))
611 {
612 wxString runInTerminal = cbC2U(node->Attribute("run_host_application_in_terminal"));
613 runHostApplicationInTerminal = (runInTerminal == wxT("1"));
614 }
615
616 // used in versions prior to 1.5
617 if (node->Attribute("includeInTargetAll"))
618 includeInTargetAll = atoi(node->Attribute("includeInTargetAll")) != 0;
619
620 if (node->Attribute("createDefFile"))
621 createDefFile = atoi(node->Attribute("createDefFile")) != 0;
622
623 if (node->Attribute("createStaticLib"))
624 createStaticLib = atoi(node->Attribute("createStaticLib")) != 0;
625
626 if (node->Attribute("projectCompilerOptionsRelation"))
627 projectCompilerOptionsRelation = atoi(node->Attribute("projectCompilerOptionsRelation"));
628
629 if (node->Attribute("projectLinkerOptionsRelation"))
630 projectLinkerOptionsRelation = atoi(node->Attribute("projectLinkerOptionsRelation"));
631
632 if (node->Attribute("projectIncludeDirsRelation"))
633 projectIncludeDirsRelation = atoi(node->Attribute("projectIncludeDirsRelation"));
634
635 if (node->Attribute("projectLibDirsRelation"))
636 projectLibDirsRelation = atoi(node->Attribute("projectLibDirsRelation"));
637
638 if (node->Attribute("projectResourceIncludeDirsRelation"))
639 {
640 projectResIncludeDirsRelation = atoi(node->Attribute("projectResourceIncludeDirsRelation"));
641 // there used to be a bug in this setting and it might have a negative or very big number
642 // detect this case and set as default
643 if (projectResIncludeDirsRelation < 0 || projectResIncludeDirsRelation >= ortLast)
644 projectResIncludeDirsRelation = 3;
645 }
646
647 node = node->NextSiblingElement("Option");
648 }
649
650 node = parentNode->FirstChildElement("Script");
651 while (node)
652 {
653 if (node->Attribute("file"))
654 target->AddBuildScript(cbC2U(node->Attribute("file")));
655
656 node = node->NextSiblingElement("Script");
657 }
658
659 if (type != -1)
660 {
661 target->SetPlatforms(platformsFinal);
662 target->SetCompilerID(compilerId);
663 target->SetTargetFilenameGenerationPolicy(prefixPolicy, extensionPolicy);
664 target->SetTargetType((TargetType)type); // type *must* come before output filename!
665 target->SetOutputFilename(output); // because if no filename defined, one will be suggested based on target type...
666 target->SetImportLibraryFilename(imp_lib);
667 target->SetDefinitionFileFilename(def_file);
668 target->SetUseConsoleRunner(use_console_runner);
669 if (!working_dir.IsEmpty())
670 target->SetWorkingDir(working_dir);
671 if (!obj_output.IsEmpty())
672 target->SetObjectOutput(obj_output);
673 if (!deps_output.IsEmpty())
674 target->SetDepsOutput(deps_output);
675 target->SetExternalDeps(deps);
676 target->SetAdditionalOutputFiles(added);
677 target->SetExecutionParameters(parameters);
678 target->SetHostApplication(hostApplication);
679 target->SetRunHostApplicationInTerminal(runHostApplicationInTerminal);
680 target->SetIncludeInTargetAll(includeInTargetAll); // used in versions prior to 1.5
681 target->SetCreateDefFile(createDefFile);
682 target->SetCreateStaticLib(createStaticLib);
683 target->SetOptionRelation(ortCompilerOptions, (OptionsRelation)projectCompilerOptionsRelation);
684 target->SetOptionRelation(ortLinkerOptions, (OptionsRelation)projectLinkerOptionsRelation);
685 target->SetOptionRelation(ortIncludeDirs, (OptionsRelation)projectIncludeDirsRelation);
686 target->SetOptionRelation(ortLibDirs, (OptionsRelation)projectLibDirsRelation);
687 target->SetOptionRelation(ortResDirs, (OptionsRelation)projectResIncludeDirsRelation);
688
689 DoMakeCommands(parentNode->FirstChildElement("MakeCommands"), target);
690 }
691 }
692
DoCompilerOptions(TiXmlElement * parentNode,ProjectBuildTarget * target)693 void ProjectLoader::DoCompilerOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
694 {
695 TiXmlElement* node = parentNode->FirstChildElement("Compiler");
696 if (!node)
697 return; // no options
698
699 TiXmlElement* child = node->FirstChildElement("Add");
700 while (child)
701 {
702 wxString option = cbC2U(child->Attribute("option"));
703 wxString dir = UnixFilename(cbC2U(child->Attribute("directory")));
704 if (!option.IsEmpty())
705 {
706 if (target)
707 target->AddCompilerOption(option);
708 else
709 m_pProject->AddCompilerOption(option);
710 }
711 if (!dir.IsEmpty())
712 {
713 if (target)
714 target->AddIncludeDir(dir);
715 else
716 m_pProject->AddIncludeDir(dir);
717 }
718
719 child = child->NextSiblingElement("Add");
720 }
721 }
722
DoResourceCompilerOptions(TiXmlElement * parentNode,ProjectBuildTarget * target)723 void ProjectLoader::DoResourceCompilerOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
724 {
725 TiXmlElement* node = parentNode->FirstChildElement("ResourceCompiler");
726 if (!node)
727 return; // no options
728
729 TiXmlElement* child = node->FirstChildElement("Add");
730 while (child)
731 {
732 wxString option = cbC2U(child->Attribute("option"));
733 wxString dir = UnixFilename(cbC2U(child->Attribute("directory")));
734 if (!option.IsEmpty())
735 {
736 if (target)
737 target->AddResourceCompilerOption(option);
738 else
739 m_pProject->AddResourceCompilerOption(option);
740 }
741 if (!dir.IsEmpty())
742 {
743 if (target)
744 target->AddResourceIncludeDir(dir);
745 else
746 m_pProject->AddResourceIncludeDir(dir);
747 }
748
749 child = child->NextSiblingElement("Add");
750 }
751 }
752
DoLinkerOptions(TiXmlElement * parentNode,ProjectBuildTarget * target)753 void ProjectLoader::DoLinkerOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
754 {
755 TiXmlElement* node = parentNode->FirstChildElement("Linker");
756 if (!node)
757 return; // no options
758
759 TiXmlElement* child = node->FirstChildElement("Add");
760 while (child)
761 {
762 wxString option = cbC2U(child->Attribute("option"));
763 wxString dir = UnixFilename(cbC2U(child->Attribute("directory")));
764 wxString lib = UnixFilename(cbC2U(child->Attribute("library")));
765 if (!option.IsEmpty())
766 {
767 if (target)
768 target->AddLinkerOption(option);
769 else
770 m_pProject->AddLinkerOption(option);
771 }
772 if (!lib.IsEmpty())
773 {
774 if (target)
775 target->AddLinkLib(lib);
776 else
777 m_pProject->AddLinkLib(lib);
778 }
779 if (!dir.IsEmpty())
780 {
781 if (target)
782 target->AddLibDir(dir);
783 else
784 m_pProject->AddLibDir(dir);
785 }
786
787 child = child->NextSiblingElement("Add");
788 }
789
790 child = node->FirstChildElement("LinkerExe");
791 if (child)
792 {
793 const wxString value = cbC2U(child->Attribute("value"));
794
795 wxString str[int(LinkerExecutableOption::Last) - 1] = {
796 wxT("CCompiler"),
797 wxT("CppCompiler"),
798 wxT("Linker")
799 };
800
801 int index;
802 for (index = 0; index < cbCountOf(str); ++index)
803 {
804 if (value == str[index])
805 break;
806 }
807
808 LinkerExecutableOption linkerExe = LinkerExecutableOption::AutoDetect;
809 if (index < cbCountOf(str))
810 linkerExe = LinkerExecutableOption(index + 1);
811
812 if (target)
813 target->SetLinkerExecutable(linkerExe);
814 }
815 }
816
DoIncludesOptions(TiXmlElement * parentNode,ProjectBuildTarget * target)817 void ProjectLoader::DoIncludesOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
818 {
819 TiXmlElement* node = parentNode->FirstChildElement("IncludeDirs");
820 if (!node)
821 return; // no options
822
823 TiXmlElement* child = node->FirstChildElement("Add");
824 while (child)
825 {
826 wxString option = UnixFilename(cbC2U(child->Attribute("option")));
827 if (!option.IsEmpty())
828 {
829 if (target)
830 target->AddIncludeDir(option);
831 else
832 m_pProject->AddIncludeDir(option);
833 }
834
835 child = child->NextSiblingElement("Add");
836 }
837 }
838
DoLibsOptions(TiXmlElement * parentNode,ProjectBuildTarget * target)839 void ProjectLoader::DoLibsOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
840 {
841 TiXmlElement* node = parentNode->FirstChildElement("LibDirs");
842 if (!node)
843 return; // no options
844
845 TiXmlElement* child = node->FirstChildElement("Add");
846 while (child)
847 {
848 wxString option = UnixFilename(cbC2U(child->Attribute("option")));
849 if (!option.IsEmpty())
850 {
851 if (target)
852 target->AddLibDir(option);
853 else
854 m_pProject->AddLibDir(option);
855 }
856
857 child = child->NextSiblingElement("Add");
858 }
859 }
860
DoExtraCommands(TiXmlElement * parentNode,ProjectBuildTarget * target)861 void ProjectLoader::DoExtraCommands(TiXmlElement* parentNode, ProjectBuildTarget* target)
862 {
863 TiXmlElement* node = parentNode->FirstChildElement("ExtraCommands");
864 while (node)
865 {
866 CompileOptionsBase* base = target ? target : (CompileOptionsBase*)m_pProject;
867 TiXmlElement* child = node->FirstChildElement("Mode");
868 while (child)
869 {
870 wxString mode = cbC2U(child->Attribute("after"));
871 if (mode == _T("always"))
872 base->SetAlwaysRunPostBuildSteps(true);
873
874 child = child->NextSiblingElement("Mode");
875 }
876
877 child = node->FirstChildElement("Add");
878 while (child)
879 {
880 wxString before;
881 wxString after;
882
883 if (child->Attribute("before"))
884 before = cbC2U(child->Attribute("before"));
885 if (child->Attribute("after"))
886 after = cbC2U(child->Attribute("after"));
887
888 if (!before.IsEmpty())
889 base->AddCommandsBeforeBuild(before);
890 if (!after.IsEmpty())
891 base->AddCommandsAfterBuild(after);
892
893 child = child->NextSiblingElement("Add");
894 }
895 node = node->NextSiblingElement("ExtraCommands");
896 }
897 }
898
DoEnvironment(TiXmlElement * parentNode,CompileOptionsBase * base)899 void ProjectLoader::DoEnvironment(TiXmlElement* parentNode, CompileOptionsBase* base)
900 {
901 if (!base)
902 return;
903
904 TiXmlElement* node = parentNode->FirstChildElement("Environment");
905 while (node)
906 {
907 TiXmlElement* child = node->FirstChildElement("Variable");
908 while (child)
909 {
910 wxString name = cbC2U(child->Attribute("name"));
911 wxString value = cbC2U(child->Attribute("value"));
912 if (!name.IsEmpty())
913 base->SetVar(name, UnixFilename(value));
914
915 child = child->NextSiblingElement("Variable");
916 }
917 node = node->NextSiblingElement("Environment");
918 }
919 }
920
921 namespace
922 {
makePathAbsoluteIfNeeded(const wxString & path,const wxString & basePath)923 wxString makePathAbsoluteIfNeeded(const wxString& path, const wxString& basePath)
924 {
925 wxString absolute = path;
926 wxFileName fname = path;
927 if (!fname.IsAbsolute())
928 {
929 fname.MakeAbsolute(basePath);
930 absolute = fname.GetFullPath();
931 }
932 return absolute;
933 }
934
makePathRelativeIfNeeded(const wxString & path,const wxString & basePath)935 wxString makePathRelativeIfNeeded(const wxString& path, const wxString& basePath)
936 {
937 wxString relative = path;
938 wxFileName fname = path;
939 if (fname.IsAbsolute())
940 {
941 fname.MakeRelativeTo(basePath);
942 relative = fname.GetFullPath();
943 }
944 return relative;
945 }
946
makePathsRelativeIfNeeded(const wxArrayString & paths,const wxString & basePath)947 wxArrayString makePathsRelativeIfNeeded(const wxArrayString& paths, const wxString& basePath)
948 {
949 wxArrayString relatives = paths;
950 for(std::size_t index = 0U; index < paths.Count(); ++index)
951 {
952 wxString& path = relatives[index];
953 path = makePathRelativeIfNeeded(path, basePath);
954 }
955 return relatives;
956 }
957
filterOnWildcards(const wxArrayString & files,const wxString & wildCard)958 std::vector<wxString> filterOnWildcards(const wxArrayString& files, const wxString& wildCard)
959 {
960 wxString wild = wildCard;
961 if(wild.IsEmpty())
962 {
963 FilesGroupsAndMasks fgm;
964 for (unsigned i = 0; i < fgm.GetGroupsCount(); ++i)
965 {
966 wild += fgm.GetFileMasks(i);
967 }
968 }
969
970 const wxArrayString wilds = GetArrayFromString(wild, _T(";"));
971 std::vector<wxString> finalFiles;
972 for(std::size_t file = 0; file < files.Count(); ++file)
973 {
974 const wxString& fileName = files[file];
975 bool MatchesWildCard = false;
976 for (std::size_t x = 0; x < wilds.GetCount(); ++x)
977 {
978 if (fileName.Matches(wilds[x].Lower()))
979 {
980 MatchesWildCard = true;
981 break;
982 }
983 }
984 if(MatchesWildCard)
985 {
986 finalFiles.push_back(fileName);
987 }
988 }
989 return finalFiles;
990 }
991
filesInDir(const wxString & directory,const wxString & wildCard,bool recursive,const wxString & basePath)992 std::vector<wxString> filesInDir(const wxString& directory, const wxString& wildCard, bool recursive, const wxString& basePath)
993 {
994 const wxString directoryPath = makePathAbsoluteIfNeeded(directory, basePath);
995 std::vector<wxString> files;
996
997 int flags = wxDIR_FILES;
998 if(recursive)
999 {
1000 flags = flags | wxDIR_DIRS;
1001 }
1002 wxArrayString filesUnfiltered;
1003 wxDir::GetAllFiles(directoryPath, &filesUnfiltered, wxEmptyString, flags);
1004 filesUnfiltered = makePathsRelativeIfNeeded(filesUnfiltered, basePath);
1005 return filterOnWildcards(filesUnfiltered, wildCard);
1006 }
1007 } // namespace
1008
DoUnits(const TiXmlElement * parentNode)1009 void ProjectLoader::DoUnits(const TiXmlElement* parentNode)
1010 {
1011 Manager::Get()->GetLogManager()->DebugLog(_T("Loading project files..."));
1012 m_pProject->BeginAddFiles();
1013
1014 int count = 0;
1015
1016 // TODO : we need to store all the globs, so that at save time we can filter files out, globs derived ones should not be stored as <Unit ... >
1017 std::vector<cbProject::Glob> unitsGlobs;
1018
1019 const std::string UnitsGlobLabel("UnitsGlob");
1020 const TiXmlElement* unitsGlob = parentNode->FirstChildElement(UnitsGlobLabel.c_str());
1021 while (unitsGlob)
1022 {
1023 const wxString directory = cbC2U(unitsGlob->Attribute("directory"));
1024 const wxString wildCard = cbC2U(unitsGlob->Attribute("wildcard"));
1025
1026 int recursive = 1;
1027 unitsGlob->QueryIntAttribute("recursive", &recursive);
1028
1029 if (!directory.IsEmpty())
1030 {
1031 const bool isRecursive = (recursive)?true:false;
1032 unitsGlobs.push_back(cbProject::Glob(directory, wildCard, isRecursive));
1033 std::vector<wxString> files = filesInDir(directory, wildCard, isRecursive, m_pProject->GetBasePath());
1034 for (std::size_t index = 0; index < files.size(); ++index)
1035 {
1036 const wxString filename = files[index];
1037 ProjectFile* file = m_pProject->AddFile(-1, UnixFilename(filename));
1038 if (!file)
1039 Manager::Get()->GetLogManager()->DebugLog(_T("Can't load file ") + filename);
1040 else
1041 {
1042 ++count;
1043 const TiXmlElement dummyUnitWithoutOptions("Unit");
1044 DoUnitOptions(&dummyUnitWithoutOptions, file);
1045 }
1046 }
1047 }
1048 unitsGlob = unitsGlob->NextSiblingElement(UnitsGlobLabel.c_str());
1049 }
1050 m_pProject->SetGlobs(unitsGlobs);
1051
1052 const TiXmlElement* unit = parentNode->FirstChildElement("Unit");
1053 while (unit)
1054 {
1055 const wxString filename = cbC2U(unit->Attribute("filename"));
1056 if (!filename.IsEmpty())
1057 {
1058 ProjectFile* file = m_pProject->AddFile(-1, UnixFilename(filename));
1059 if (!file)
1060 Manager::Get()->GetLogManager()->DebugLog(_T("Can't load file ") + filename);
1061 else
1062 {
1063 ++count;
1064 DoUnitOptions(unit, file);
1065 }
1066 }
1067
1068 unit = unit->NextSiblingElement("Unit");
1069 }
1070 m_pProject->EndAddFiles();
1071 Manager::Get()->GetLogManager()->DebugLog(F(_T("%d files loaded"), count));
1072 }
1073
DoUnitOptions(const TiXmlElement * parentNode,ProjectFile * file)1074 void ProjectLoader::DoUnitOptions(const TiXmlElement* parentNode, ProjectFile* file)
1075 {
1076 int tempval = 0;
1077 bool foundCompile = false;
1078 bool foundLink = false;
1079 bool foundCompilerVar = false;
1080 bool foundTarget = false;
1081 bool noTarget = false;
1082
1083 // Compiler* compiler = CompilerFactory::GetCompiler(m_pProject->GetCompilerID());
1084
1085 const TiXmlElement* node = parentNode->FirstChildElement("Option");
1086 while (node)
1087 {
1088 if (node->Attribute("compilerVar"))
1089 {
1090 file->compilerVar = cbC2U(node->Attribute("compilerVar"));
1091 foundCompilerVar = true;
1092 }
1093 //
1094 if (node->QueryIntAttribute("compile", &tempval) == TIXML_SUCCESS)
1095 {
1096 file->compile = tempval != 0;
1097 foundCompile = true;
1098 }
1099 //
1100 if (node->QueryIntAttribute("link", &tempval) == TIXML_SUCCESS)
1101 {
1102 file->link = tempval != 0;
1103 foundLink = true;
1104 }
1105 //
1106 if (node->QueryIntAttribute("weight", &tempval) == TIXML_SUCCESS)
1107 file->weight = tempval;
1108 //
1109 if (node->Attribute("virtualFolder"))
1110 file->virtual_path = UnixFilename(cbC2U(node->Attribute("virtualFolder")));
1111 //
1112 if (node->Attribute("buildCommand") && node->Attribute("compiler"))
1113 {
1114 const wxString cmp = cbC2U(node->Attribute("compiler"));
1115 wxString tmp = cbC2U(node->Attribute("buildCommand"));
1116 if (!cmp.IsEmpty() && !tmp.IsEmpty())
1117 {
1118 tmp.Replace(_T("\\n"), _T("\n"));
1119 file->SetCustomBuildCommand(cmp, tmp);
1120 if (node->QueryIntAttribute("use", &tempval) == TIXML_SUCCESS)
1121 file->SetUseCustomBuildCommand(cmp, tempval != 0);
1122 }
1123 }
1124 //
1125 if (node->Attribute("target"))
1126 {
1127 wxString targetName = cbC2U(node->Attribute("target"));
1128 if (!targetName.IsSameAs(_T("<{~None~}>")))
1129 {
1130 file->AddBuildTarget(targetName);
1131 foundTarget = true;
1132 }
1133 else
1134 noTarget = true;
1135 }
1136
1137 node = node->NextSiblingElement("Option");
1138 }
1139
1140 // pre 1.6 versions upgrade
1141 if (m_IsPre_1_6)
1142 {
1143 // make sure the "compile" and "link" flags are honored
1144 if (!foundCompile)
1145 file->compile = true;
1146 if (!foundLink)
1147 file->link = true;
1148 if (!foundCompilerVar)
1149 file->compilerVar = _T("CPP");
1150 }
1151
1152 if (!foundTarget && !noTarget)
1153 {
1154 // add to all targets
1155 for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
1156 {
1157 file->AddBuildTarget(m_pProject->GetBuildTarget(i)->GetTitle());
1158 }
1159 }
1160 }
1161
1162 // convenience function, used in Save()
AddElement(TiXmlElement * parent,const char * name,const char * attr=nullptr,const wxString & attribute=wxEmptyString)1163 static TiXmlElement* AddElement(TiXmlElement* parent, const char* name, const char* attr = nullptr,
1164 const wxString& attribute = wxEmptyString)
1165 {
1166 TiXmlElement elem(name);
1167
1168 if (attr)
1169 elem.SetAttribute(attr, cbU2C(attribute));
1170
1171 return parent->InsertEndChild(elem)->ToElement();
1172 }
1173
1174 // convenience function, used in Save()
AddElement(TiXmlElement * parent,const char * name,const char * attr,int attribute)1175 static TiXmlElement* AddElement(TiXmlElement* parent, const char* name, const char* attr,
1176 int attribute)
1177 {
1178 TiXmlElement elem(name);
1179
1180 if (attr)
1181 elem.SetAttribute(attr, attribute);
1182
1183 return parent->InsertEndChild(elem)->ToElement();
1184 }
1185
1186 // convenience function, used in Save()
AddArrayOfElements(TiXmlElement * parent,const char * name,const char * attr,const wxArrayString & array,bool isPath=false)1187 static void AddArrayOfElements(TiXmlElement* parent, const char* name, const char* attr,
1188 const wxArrayString& array, bool isPath = false)
1189 {
1190 if (!array.GetCount())
1191 return;
1192
1193 for (unsigned int i = 0; i < array.GetCount(); ++i)
1194 {
1195 if (array[i].IsEmpty())
1196 continue;
1197 AddElement(parent, name, attr, (isPath ? UnixFilename(array[i], wxPATH_UNIX) : array[i]));
1198 }
1199 }
1200
1201 // convenience function, used in Save()
SaveEnvironment(TiXmlElement * parent,CompileOptionsBase * base)1202 static void SaveEnvironment(TiXmlElement* parent, CompileOptionsBase* base)
1203 {
1204 if (!base)
1205 return;
1206 const StringHash& v = base->GetAllVars();
1207 if (v.empty())
1208 return;
1209
1210 // explicitly sort the keys
1211 typedef std::map<wxString, wxString> SortedMap;
1212 SortedMap map;
1213 for (StringHash::const_iterator it = v.begin(); it != v.end(); ++it)
1214 map[it->first] = it->second;
1215
1216 TiXmlElement* node = AddElement(parent, "Environment");
1217 for (SortedMap::const_iterator it = map.begin(); it != map.end(); ++it)
1218 {
1219 TiXmlElement* elem = AddElement(node, "Variable", "name", it->first);
1220 elem->SetAttribute("value", cbU2C(it->second));
1221 }
1222 }
1223
Save(const wxString & filename)1224 bool ProjectLoader::Save(const wxString& filename)
1225 {
1226 return Save(filename, nullptr);
1227 }
1228
Save(const wxString & filename,TiXmlElement * pExtensions)1229 bool ProjectLoader::Save(const wxString& filename, TiXmlElement* pExtensions)
1230 {
1231 if (ExportTargetAsProject(filename, wxEmptyString, pExtensions))
1232 {
1233 m_pProject->SetModified(false);
1234 return true;
1235 }
1236 return false;
1237 }
1238
1239 // convenience function, used in ExportTargetAsProject()
SaveLinkerExecutable(TiXmlElement * linkerNode,const CompileOptionsBase & options)1240 static void SaveLinkerExecutable(TiXmlElement *linkerNode, const CompileOptionsBase &options)
1241 {
1242 const LinkerExecutableOption linkerExe = options.GetLinkerExecutable();
1243 if (linkerExe > LinkerExecutableOption::AutoDetect
1244 && linkerExe < LinkerExecutableOption::Last)
1245 {
1246 wxString str[int(LinkerExecutableOption::Last) - 1] = {
1247 wxT("CCompiler"),
1248 wxT("CppCompiler"),
1249 wxT("Linker")
1250 };
1251 AddElement(linkerNode, "LinkerExe", "value", str[int(linkerExe) - 1]);
1252 }
1253 }
1254
ExportTargetAsProject(const wxString & filename,const wxString & onlyTarget,TiXmlElement * pExtensions)1255 bool ProjectLoader::ExportTargetAsProject(const wxString& filename, const wxString& onlyTarget, TiXmlElement* pExtensions)
1256 {
1257 const char* ROOT_TAG = "CodeBlocks_project_file";
1258
1259 TiXmlDocument doc;
1260 doc.SetCondenseWhiteSpace(false);
1261 doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
1262 TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
1263 if (!rootnode)
1264 return false;
1265
1266 // Compiler* compiler = CompilerFactory::GetCompiler(m_pProject->GetCompilerID());
1267
1268 rootnode->InsertEndChild(TiXmlElement("FileVersion"));
1269 rootnode->FirstChildElement("FileVersion")->SetAttribute("major", PROJECT_FILE_VERSION_MAJOR);
1270 rootnode->FirstChildElement("FileVersion")->SetAttribute("minor", PROJECT_FILE_VERSION_MINOR);
1271
1272 rootnode->InsertEndChild(TiXmlElement("Project"));
1273 TiXmlElement* prjnode = rootnode->FirstChildElement("Project");
1274
1275 AddElement(prjnode, "Option", "title", m_pProject->GetTitle());
1276 if (m_pProject->GetPlatforms() != spAll)
1277 {
1278 wxString platforms = GetStringFromPlatforms(m_pProject->GetPlatforms());
1279 AddElement(prjnode, "Option", "platforms", platforms);
1280 }
1281 if (m_pProject->GetMakefile() != _T("Makefile"))
1282 AddElement(prjnode, "Option", "makefile", UnixFilename(m_pProject->GetMakefile(), wxPATH_UNIX));
1283 if (m_pProject->IsMakefileCustom())
1284 AddElement(prjnode, "Option", "makefile_is_custom", 1);
1285 if (m_pProject->GetMakefileExecutionDir() != m_pProject->GetBasePath())
1286 AddElement(prjnode, "Option", "execution_dir", UnixFilename(m_pProject->GetMakefileExecutionDir(), wxPATH_UNIX));
1287 if (m_pProject->GetModeForPCH() != pchObjectDir)
1288 AddElement(prjnode, "Option", "pch_mode", (int)m_pProject->GetModeForPCH());
1289 if (!m_pProject->GetDefaultExecuteTarget().IsEmpty() && m_pProject->GetDefaultExecuteTarget() != m_pProject->GetFirstValidBuildTargetName())
1290 AddElement(prjnode, "Option", "default_target", m_pProject->GetDefaultExecuteTarget());
1291 AddElement(prjnode, "Option", "compiler", m_pProject->GetCompilerID());
1292
1293 wxArrayString virtualFolders = m_pProject->GetVirtualFolders();
1294 if (virtualFolders.GetCount() > 0)
1295 {
1296 wxString result; // the concatenated string
1297 for (size_t i = 0; i < virtualFolders.GetCount(); i++)
1298 {
1299 if (!result.IsEmpty())
1300 result << wxT(";"); // add the delimiter
1301
1302 result << UnixFilename(virtualFolders[i], wxPATH_UNIX); // append Unix format folder name
1303 }
1304 AddElement(prjnode, "Option", "virtualFolders", result);
1305 }
1306
1307 if (m_pProject->GetExtendedObjectNamesGeneration())
1308 AddElement(prjnode, "Option", "extended_obj_names", 1);
1309 if (m_pProject->GetShowNotesOnLoad() || !m_pProject->GetNotes().IsEmpty())
1310 {
1311 TiXmlElement* notesBase = AddElement(prjnode, "Option", "show_notes", m_pProject->GetShowNotesOnLoad() ? 1 : 0);
1312 if (!m_pProject->GetNotes().IsEmpty())
1313 {
1314 TiXmlElement* notes = AddElement(notesBase, "notes");
1315 TiXmlText t(m_pProject->GetNotes().mb_str(wxConvUTF8));
1316 t.SetCDATA(true);
1317 notes->InsertEndChild(t);
1318 }
1319 }
1320 if (!m_pProject->GetCheckForExternallyModifiedFiles())
1321 AddElement(prjnode, "Option", "check_files", 0);
1322
1323 if (m_pProject->MakeCommandsModified())
1324 {
1325 TiXmlElement* makenode = AddElement(prjnode, "MakeCommands");
1326 AddElement(makenode, "Build", "command", m_pProject->GetMakeCommandFor(mcBuild));
1327 AddElement(makenode, "CompileFile", "command", m_pProject->GetMakeCommandFor(mcCompileFile));
1328 AddElement(makenode, "Clean", "command", m_pProject->GetMakeCommandFor(mcClean));
1329 AddElement(makenode, "DistClean", "command", m_pProject->GetMakeCommandFor(mcDistClean));
1330 AddElement(makenode, "AskRebuildNeeded", "command", m_pProject->GetMakeCommandFor(mcAskRebuildNeeded));
1331 AddElement(makenode, "SilentBuild", "command", m_pProject->GetMakeCommandFor(mcSilentBuild));
1332 }
1333
1334 prjnode->InsertEndChild(TiXmlElement("Build"));
1335 TiXmlElement* buildnode = prjnode->FirstChildElement("Build");
1336
1337 for (size_t x = 0; x < m_pProject->GetBuildScripts().GetCount(); ++x)
1338 AddElement(buildnode, "Script", "file", UnixFilename(m_pProject->GetBuildScripts().Item(x), wxPATH_UNIX));
1339
1340 // now decide which target we're exporting.
1341 // remember that if onlyTarget is empty, we export all targets (i.e. normal save).
1342 ProjectBuildTarget* onlytgt = m_pProject->GetBuildTarget(onlyTarget);
1343
1344 for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
1345 {
1346 ProjectBuildTarget* target = m_pProject->GetBuildTarget(i);
1347 if (!target)
1348 break;
1349
1350 // skip every target except the desired one
1351 if (onlytgt && onlytgt != target)
1352 continue;
1353
1354 TiXmlElement* tgtnode = AddElement(buildnode, "Target", "title", target->GetTitle());
1355 if (target->GetPlatforms() != spAll)
1356 {
1357 wxString platforms = GetStringFromPlatforms(target->GetPlatforms());
1358 AddElement(tgtnode, "Option", "platforms", platforms);
1359 }
1360 if (target->GetTargetType() != ttCommandsOnly)
1361 {
1362 TargetFilenameGenerationPolicy prefixPolicy;
1363 TargetFilenameGenerationPolicy extensionPolicy;
1364 target->GetTargetFilenameGenerationPolicy(prefixPolicy, extensionPolicy);
1365
1366 wxString outputFileName = target->GetOutputFilename();
1367 if (extensionPolicy == tgfpPlatformDefault)
1368 {
1369 wxFileName fname(outputFileName);
1370 fname.ClearExt();
1371 outputFileName = fname.GetFullPath();
1372 }
1373
1374 if ( (prefixPolicy == tgfpPlatformDefault)
1375 && ( (!platform::windows && target->GetTargetType() == ttDynamicLib)
1376 || (target->GetTargetType() == ttStaticLib) ) )
1377 {
1378 wxString compilerId = target->GetCompilerID();
1379 Compiler* compiler = CompilerFactory::GetCompiler(compilerId);
1380 if (compiler)
1381 {
1382 wxFileName fname(outputFileName);
1383 wxString outputFileNameFile(fname.GetFullName());
1384
1385 wxString compilerLibPrefix(compiler->GetSwitches().libPrefix);
1386 wxString outputFileNameWOPrefix;
1387 if (outputFileNameFile.StartsWith(compilerLibPrefix))
1388 {
1389 outputFileNameWOPrefix = outputFileNameFile.Mid(compilerLibPrefix.Len());
1390 if (!outputFileNameWOPrefix.IsEmpty())
1391 {
1392 fname.SetFullName(outputFileNameWOPrefix);
1393 outputFileName = fname.GetFullPath();
1394 }
1395 }
1396 }
1397 }
1398
1399 TiXmlElement* outnode = AddElement(tgtnode, "Option", "output", UnixFilename(outputFileName, wxPATH_UNIX));
1400 if (target->GetTargetType() == ttDynamicLib)
1401 {
1402 if (target->GetDynamicLibImportFilename() != _T("$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME)"))
1403 outnode->SetAttribute("imp_lib", cbU2C(UnixFilename(target->GetDynamicLibImportFilename(), wxPATH_UNIX)));
1404 if (target->GetDynamicLibImportFilename() != _T("$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME)"))
1405 outnode->SetAttribute("def_file", cbU2C(UnixFilename(target->GetDynamicLibDefFilename(), wxPATH_UNIX)));
1406 }
1407 outnode->SetAttribute("prefix_auto", prefixPolicy == tgfpPlatformDefault ? "1" : "0");
1408 outnode->SetAttribute("extension_auto", extensionPolicy == tgfpPlatformDefault ? "1" : "0");
1409
1410 if (target->GetWorkingDir() != _T("."))
1411 AddElement(tgtnode, "Option", "working_dir", UnixFilename(target->GetWorkingDir(), wxPATH_UNIX));
1412 if (target->GetObjectOutput() != _T(".objs"))
1413 AddElement(tgtnode, "Option", "object_output", UnixFilename(target->GetObjectOutput(), wxPATH_UNIX));
1414 if (target->GetDepsOutput() != _T(".deps"))
1415 AddElement(tgtnode, "Option", "deps_output", UnixFilename(target->GetDepsOutput(), wxPATH_UNIX));
1416 }
1417 if (!target->GetExternalDeps().IsEmpty())
1418 AddElement(tgtnode, "Option", "external_deps", UnixFilename(target->GetExternalDeps(), wxPATH_UNIX));
1419 if (!target->GetAdditionalOutputFiles().IsEmpty())
1420 AddElement(tgtnode, "Option", "additional_output", UnixFilename(target->GetAdditionalOutputFiles(), wxPATH_UNIX));
1421 AddElement(tgtnode, "Option", "type", target->GetTargetType());
1422 AddElement(tgtnode, "Option", "compiler", target->GetCompilerID());
1423 if (target->GetTargetType() == ttConsoleOnly && !target->GetUseConsoleRunner())
1424 AddElement(tgtnode, "Option", "use_console_runner", 0);
1425 if (!target->GetExecutionParameters().IsEmpty())
1426 AddElement(tgtnode, "Option", "parameters", target->GetExecutionParameters());
1427 if (!target->GetHostApplication().IsEmpty())
1428 {
1429 AddElement(tgtnode, "Option", "host_application", UnixFilename(target->GetHostApplication(), wxPATH_UNIX));
1430 if (target->GetRunHostApplicationInTerminal())
1431 AddElement(tgtnode, "Option", "run_host_application_in_terminal", 1);
1432 else
1433 AddElement(tgtnode, "Option", "run_host_application_in_terminal", 0);
1434 }
1435
1436 // used in versions prior to 1.5
1437 // if (target->GetIncludeInTargetAll())
1438 // AddElement(tgtnode, "Option", "includeInTargetAll", 1);
1439 if ((target->GetTargetType() == ttStaticLib || target->GetTargetType() == ttDynamicLib) && target->GetCreateDefFile())
1440 AddElement(tgtnode, "Option", "createDefFile", 1);
1441 if (target->GetTargetType() == ttDynamicLib && target->GetCreateStaticLib())
1442 AddElement(tgtnode, "Option", "createStaticLib", 1);
1443 if (target->GetOptionRelation(ortCompilerOptions) != 3) // 3 is the default
1444 AddElement(tgtnode, "Option", "projectCompilerOptionsRelation", target->GetOptionRelation(ortCompilerOptions));
1445 if (target->GetOptionRelation(ortLinkerOptions) != 3) // 3 is the default
1446 AddElement(tgtnode, "Option", "projectLinkerOptionsRelation", target->GetOptionRelation(ortLinkerOptions));
1447 if (target->GetOptionRelation(ortIncludeDirs) != 3) // 3 is the default
1448 AddElement(tgtnode, "Option", "projectIncludeDirsRelation", target->GetOptionRelation(ortIncludeDirs));
1449 if (target->GetOptionRelation(ortResDirs) != 3) // 3 is the default
1450 AddElement(tgtnode, "Option", "projectResourceIncludeDirsRelation", target->GetOptionRelation(ortResDirs));
1451 if (target->GetOptionRelation(ortLibDirs) != 3) // 3 is the default
1452 AddElement(tgtnode, "Option", "projectLibDirsRelation", target->GetOptionRelation(ortLibDirs));
1453
1454 for (size_t x = 0; x < target->GetBuildScripts().GetCount(); ++x)
1455 AddElement(tgtnode, "Script", "file", target->GetBuildScripts().Item(x));
1456
1457 TiXmlElement* node = AddElement(tgtnode, "Compiler");
1458 AddArrayOfElements(node, "Add", "option", target->GetCompilerOptions());
1459 AddArrayOfElements(node, "Add", "directory", target->GetIncludeDirs(), true);
1460 if (node->NoChildren())
1461 tgtnode->RemoveChild(node);
1462
1463 node = AddElement(tgtnode, "ResourceCompiler");
1464 AddArrayOfElements(node, "Add", "option", target->GetResourceCompilerOptions());
1465 AddArrayOfElements(node, "Add", "directory", target->GetResourceIncludeDirs(), true);
1466 if (node->NoChildren())
1467 tgtnode->RemoveChild(node);
1468
1469 node = AddElement(tgtnode, "Linker");
1470 AddArrayOfElements(node, "Add", "option", target->GetLinkerOptions());
1471 AddArrayOfElements(node, "Add", "library", target->GetLinkLibs(), true);
1472 AddArrayOfElements(node, "Add", "directory", target->GetLibDirs(), true);
1473 SaveLinkerExecutable(node, *target);
1474 if (node->NoChildren())
1475 tgtnode->RemoveChild(node);
1476
1477 node = AddElement(tgtnode, "ExtraCommands");
1478 AddArrayOfElements(node, "Add", "before", target->GetCommandsBeforeBuild());
1479 AddArrayOfElements(node, "Add", "after", target->GetCommandsAfterBuild());
1480 if (node->NoChildren())
1481 tgtnode->RemoveChild(node);
1482 else
1483 {
1484 if (target->GetAlwaysRunPostBuildSteps())
1485 AddElement(node, "Mode", "after", wxString(_T("always")));
1486 }
1487
1488 SaveEnvironment(tgtnode, target);
1489
1490 if (target->MakeCommandsModified())
1491 {
1492 TiXmlElement* makenode = AddElement(tgtnode, "MakeCommands");
1493 AddElement(makenode, "Build", "command", target->GetMakeCommandFor(mcBuild));
1494 AddElement(makenode, "CompileFile", "command", target->GetMakeCommandFor(mcCompileFile));
1495 AddElement(makenode, "Clean", "command", target->GetMakeCommandFor(mcClean));
1496 AddElement(makenode, "DistClean", "command", target->GetMakeCommandFor(mcDistClean));
1497 AddElement(makenode, "AskRebuildNeeded", "command", target->GetMakeCommandFor(mcAskRebuildNeeded));
1498 AddElement(makenode, "SilentBuild", "command", target->GetMakeCommandFor(mcSilentBuild));
1499 }
1500 }
1501
1502 // virtuals only for whole project
1503 if (onlyTarget.IsEmpty())
1504 {
1505 TiXmlElement* virtnode = AddElement(prjnode, "VirtualTargets");
1506 wxArrayString virtuals = m_pProject->GetVirtualBuildTargets();
1507 for (size_t i = 0; i < virtuals.GetCount(); ++i)
1508 {
1509 const wxArrayString& group = m_pProject->GetVirtualBuildTargetGroup(virtuals[i]);
1510 wxString groupStr = GetStringFromArray(group, _T(";"));
1511 if (!groupStr.IsEmpty())
1512 {
1513 TiXmlElement* elem = AddElement(virtnode, "Add", "alias", virtuals[i]);
1514 elem->SetAttribute("targets", cbU2C(groupStr));
1515 }
1516 }
1517 if (virtnode->NoChildren())
1518 prjnode->RemoveChild(virtnode);
1519 }
1520
1521 SaveEnvironment(buildnode, m_pProject);
1522
1523 TiXmlElement* node = AddElement(prjnode, "Compiler");
1524 AddArrayOfElements(node, "Add", "option", m_pProject->GetCompilerOptions());
1525 AddArrayOfElements(node, "Add", "directory", m_pProject->GetIncludeDirs(), true);
1526 if (node->NoChildren())
1527 prjnode->RemoveChild(node);
1528
1529 node = AddElement(prjnode, "ResourceCompiler");
1530 AddArrayOfElements(node, "Add", "option", m_pProject->GetResourceCompilerOptions());
1531 AddArrayOfElements(node, "Add", "directory", m_pProject->GetResourceIncludeDirs(), true);
1532 if (node->NoChildren())
1533 prjnode->RemoveChild(node);
1534
1535 node = AddElement(prjnode, "Linker");
1536 AddArrayOfElements(node, "Add", "option", m_pProject->GetLinkerOptions());
1537 AddArrayOfElements(node, "Add", "library", m_pProject->GetLinkLibs(), true);
1538 AddArrayOfElements(node, "Add", "directory", m_pProject->GetLibDirs(), true);
1539 if (node->NoChildren())
1540 prjnode->RemoveChild(node);
1541
1542 node = AddElement(prjnode, "ExtraCommands");
1543 AddArrayOfElements(node, "Add", "before", m_pProject->GetCommandsBeforeBuild());
1544 AddArrayOfElements(node, "Add", "after", m_pProject->GetCommandsAfterBuild());
1545 if (node->NoChildren())
1546 prjnode->RemoveChild(node);
1547 else
1548 {
1549 if (m_pProject->GetAlwaysRunPostBuildSteps())
1550 AddElement(node, "Mode", "after", wxString(_T("always")));
1551 }
1552
1553 std::vector<wxString> filesThrougGlobs;
1554 const std::vector<cbProject::Glob>& unitGlobs = m_pProject->GetGlobs();
1555 for (std::size_t index = 0; index < unitGlobs.size(); ++index)
1556 {
1557 const cbProject::Glob& glob = unitGlobs[index];
1558 if (TiXmlElement* unitsGlobNode = AddElement(prjnode, "UnitsGlob", "directory", glob.m_Path))
1559 {
1560 unitsGlobNode->SetAttribute("recursive", glob.m_Recursive ? "1" : "0");
1561 unitsGlobNode->SetAttribute("wildcard", cbU2C(glob.m_WildCard));
1562 }
1563 std::vector<wxString> files = filesInDir(glob.m_Path, glob.m_WildCard, glob.m_Recursive, m_pProject->GetBasePath());
1564 std::copy(files.begin(), files.end(), std::back_inserter(filesThrougGlobs));
1565 }
1566
1567 ProjectFileArray pfa(ProjectFile::CompareProjectFiles);
1568
1569 for (FilesList::iterator it = m_pProject->GetFilesList().begin(); it != m_pProject->GetFilesList().end(); ++it)
1570 {
1571 ProjectFile* f = *it;
1572
1573 // do not save auto-generated files
1574 if (f->AutoGeneratedBy())
1575 continue;
1576
1577 if (std::find(filesThrougGlobs.begin(), filesThrougGlobs.end(), f->relativeFilename) != filesThrougGlobs.end())
1578 continue;
1579
1580 // do not save project files that do not belong in the target we 're exporting
1581 if (onlytgt && (onlytgt->GetFilesList().find(f) == onlytgt->GetFilesList().end()))
1582 continue;
1583
1584 pfa.Add(f);
1585 }
1586 for (size_t i=0; i<pfa.GetCount(); ++i)
1587 {
1588 ProjectFile* f = pfa[i];
1589 FileType ft = FileTypeOf(f->relativeFilename);
1590
1591 TiXmlElement* unitnode = AddElement(prjnode, "Unit", "filename", UnixFilename(f->relativeFilename, wxPATH_UNIX));
1592 if (!f->compilerVar.IsEmpty())
1593 {
1594 const wxString ext = f->relativeFilename.AfterLast(_T('.')).Lower();
1595 if (f->compilerVar != _T("CC") && (ext.IsSameAs(FileFilters::C_EXT)))
1596 AddElement(unitnode, "Option", "compilerVar", f->compilerVar);
1597 #ifdef __WXMSW__
1598 else if (f->compilerVar != _T("WINDRES") && ext.IsSameAs(FileFilters::RESOURCE_EXT))
1599 AddElement(unitnode, "Option", "compilerVar", f->compilerVar);
1600 #endif
1601 else if (f->compilerVar != _T("CPP")) // default
1602 AddElement(unitnode, "Option", "compilerVar", f->compilerVar);
1603 }
1604
1605 if (f->compile != (ft == ftSource || ft == ftResource))
1606 AddElement(unitnode, "Option", "compile", f->compile ? 1 : 0);
1607
1608 if (f->link != ( ft == ftSource || ft == ftResource
1609 || ft == ftObject || ft == ftResourceBin
1610 || ft == ftStaticLib ) )
1611 {
1612 AddElement(unitnode, "Option", "link", f->link ? 1 : 0);
1613 }
1614 if (f->weight != 50)
1615 AddElement(unitnode, "Option", "weight", f->weight);
1616
1617 if (!f->virtual_path.IsEmpty())
1618 AddElement(unitnode, "Option", "virtualFolder", UnixFilename(f->virtual_path, wxPATH_UNIX));
1619
1620 // loop and save custom build commands
1621 for (pfCustomBuildMap::iterator it = f->customBuild.begin(); it != f->customBuild.end(); ++it)
1622 {
1623 pfCustomBuild& pfcb = it->second;
1624 if (!pfcb.buildCommand.IsEmpty())
1625 {
1626 wxString tmp = pfcb.buildCommand;
1627 tmp.Replace(_T("\n"), _T("\\n"));
1628 TiXmlElement* elem = AddElement(unitnode, "Option", "compiler", it->first);
1629 elem->SetAttribute("use", pfcb.useCustomBuildCommand ? "1" : "0");
1630 elem->SetAttribute("buildCommand", cbU2C(tmp));
1631 }
1632 }
1633
1634 if ((int)f->buildTargets.GetCount() != m_pProject->GetBuildTargetsCount())
1635 {
1636 for (unsigned int x = 0; x < f->buildTargets.GetCount(); ++x)
1637 AddElement(unitnode, "Option", "target", f->buildTargets[x]);
1638 }
1639
1640 /* Add a target with a weird name if no targets are present. *
1641 * This will help us detecting a file with no targets. */
1642 if ((int)f->buildTargets.GetCount() == 0)
1643 AddElement(unitnode, "Option", "target", _T("<{~None~}>"));
1644 }
1645
1646 // as a last step, run all hooked callbacks
1647 TiXmlElement* extnode = pExtensions
1648 ? prjnode->InsertEndChild(*pExtensions)->ToElement()
1649 : AddElement(prjnode, "Extensions");
1650 if (ProjectLoaderHooks::HasRegisteredHooks() && extnode)
1651 ProjectLoaderHooks::CallHooks(m_pProject, extnode, false);
1652
1653 // Sort by tag name and if tag names are equal use the original index of the element in the
1654 // extensions node. Do this because we assume the code which has inserted them has done so in
1655 // some sorted manner.
1656 // Ideally there would be no duplicates.
1657
1658 struct Key
1659 {
1660 std::string tagName;
1661 int index;
1662
1663 bool operator<(const Key &o) const
1664 {
1665 if (tagName == o.tagName)
1666 return index < o.index;
1667 return tagName < o.tagName;
1668 }
1669 };
1670 std::map<Key, TiXmlNode*> sortedExtensions;
1671 int index = 0;
1672 for (TiXmlNode *child = extnode->FirstChild(); child; child = child->NextSibling(), ++index)
1673 {
1674 TiXmlElement *element = child->ToElement();
1675 if (!element)
1676 continue;
1677 // Skip empty elements, because we don't want to pollute the project file.
1678 if (element->NoChildren() && element->FirstAttribute() == nullptr)
1679 continue;
1680
1681 sortedExtensions.emplace(Key{element->Value(), index}, element->Clone());
1682 }
1683
1684 // Clear the old node and place the elements in sorted order.
1685 // We assume there are only element xml nodes, everything else would be lost.
1686 extnode->Clear();
1687 for (const std::map<Key, TiXmlNode*>::value_type &ext : sortedExtensions)
1688 {
1689 extnode->LinkEndChild(ext.second);
1690 }
1691
1692 return cbSaveTinyXMLDocument(&doc, filename);
1693 }
1694
GetValidCompilerID(const wxString & proposal,const wxString & scope)1695 wxString ProjectLoader::GetValidCompilerID(const wxString& proposal, const wxString& scope)
1696 {
1697 if (CompilerFactory::GetCompiler(proposal))
1698 return proposal;
1699
1700 // check the map; maybe we asked the user before
1701 CompilerSubstitutes::iterator it = m_CompilerSubstitutes.find(proposal);
1702 if (it != m_CompilerSubstitutes.end())
1703 return it->second;
1704
1705 Compiler* compiler = nullptr;
1706
1707 // if compiler is a number, then this is an older version of the project file
1708 // propose the same compiler by index
1709 if (!proposal.IsEmpty())
1710 {
1711 long int idx = -1;
1712 if (proposal.ToLong(&idx))
1713 compiler = CompilerFactory::GetCompiler(idx);
1714 }
1715
1716 if (!compiler)
1717 {
1718 if(!(Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/ignore_invalid_targets"), true)))
1719 {
1720 wxString msg;
1721 msg.Printf(_("The defined compiler for %s cannot be located (ID: %s).\n"
1722 "Please choose the compiler you want to use instead and click \"OK\".\n"
1723 "If you click \"Cancel\", the project/target will be excluded from the build."), scope.c_str(),
1724 proposal.c_str());
1725 compiler = CompilerFactory::SelectCompilerUI(msg);
1726 }
1727 }
1728
1729 if (!compiler)
1730 {
1731 // allow for invalid compiler IDs to be preserved...
1732 m_CompilerSubstitutes[proposal] = proposal;
1733 return proposal;
1734 }
1735
1736 m_OpenDirty = true;
1737
1738 // finally, keep the user selection in the map so we don't ask him again
1739 m_CompilerSubstitutes[proposal] = compiler->GetID();
1740 return compiler->GetID();
1741 }
1742