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