9165f07118d07f5d0b889c1609502adaa21a55f8
[sdk] / ide / src / project / ProjectNode.ec
1 #ifndef MAKEFILE_GENERATOR
2 import "ide"
3 #else
4 #ifdef ECERE_STATIC
5 import static "ecere"
6 #else
7 import "ecere"
8 #endif
9
10 import "Project"
11
12 static define app = ((GuiApplication)__thisModule);
13 #endif
14
15 #define OPTION(x) ((uint)(&((ProjectOptions)0).x))
16
17 static void OutputLog(const char * string)
18 {
19 #ifdef MAKEFILE_GENERATOR
20    printf(string);
21 #else
22    ide.outputView.buildBox.Log(string);
23 #endif
24 }
25
26 bool eString_PathInsideOfMore(const char * path, const char * of, char * pathRest)
27 {
28    if(!path[0] || !of[0])
29       return false;  // What to do here? Ever used?
30    else
31    {
32       char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION];
33       char pathPart[MAX_FILENAME]; //, pathRest[MAX_LOCATION];
34       strcpy(ofRest, of);
35       strcpy(pathRest, path);
36       for(; ofRest[0] && pathRest[0];)
37       {
38          SplitDirectory(ofRest, ofPart, ofRest);
39          SplitDirectory(pathRest, pathPart, pathRest);
40          if(fstrcmp(pathPart, ofPart))
41             return false;
42       }
43       if(!ofRest[0] && !pathRest[0])
44          return false;
45       else if(!pathRest[0])           // not inside of, it's the other way around
46          return false;
47       return true;
48    }
49 }
50
51 enum NodeTypes { project, file, folder, resources, folderOpen };
52 enum NodeIcons
53 {
54    genFile, ewsFile, epjFile, folder, openFolder, ecFile, ehFile,
55    sFile, cFile, hFile, cppFile, hppFile, textFile, webFile, pictureFile, soundFile,
56    archiveFile, packageFile, opticalMediaImageFile, mFile, mmFile;
57
58    NodeIcons ::SelectFileIcon(const char * filePath)
59    {
60       NodeIcons icon;
61       if(filePath && filePath[0])
62       {
63          char extension[MAX_EXTENSION];
64          GetExtension(filePath, extension);
65          if(strlen(extension))
66          {
67             if(!strcmpi(extension, WorkspaceExtension))
68                icon = ewsFile;
69             else if(!strcmpi(extension, ProjectExtension))
70                icon = epjFile;
71             else if(!strcmpi(extension, "ec"))
72                icon = ecFile;
73             else if(!strcmpi(extension, "eh"))
74                icon = ehFile;
75             else if(!strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
76                   !strcmpi(extension, "cxx"))
77                icon = cppFile;
78             else if(!strcmpi(extension, "hpp") || !strcmpi(extension, "hh") ||
79                   !strcmpi(extension, "hxx"))
80                icon = hppFile;
81             else if(!strcmpi(extension, "s"))
82                icon = sFile;
83             else if(!strcmpi(extension, "c"))
84                icon = cFile;
85             else if(!strcmpi(extension, "h"))
86                icon = hFile;
87             else if(!strcmpi(extension, "m"))
88                icon = mFile;
89             else if(!strcmpi(extension, "mm"))
90                icon = mmFile;
91             else if(!strcmpi(extension, "txt") || !strcmpi(extension, "text") ||
92                   !strcmpi(extension, "nfo") || !strcmpi(extension, "info"))
93                icon = textFile;
94             else if(!strcmpi(extension, "htm") || !strcmpi(extension, "html") ||
95                   !strcmpi(extension, "css") || !strcmpi(extension, "php") ||
96                   !strcmpi(extension, "js"))
97                icon = webFile;
98             else if(!strcmpi(extension, "bmp") || !strcmpi(extension, "pcx") ||
99                   !strcmpi(extension, "jpg") || !strcmpi(extension, "jpeg") ||
100                   !strcmpi(extension, "gif") || !strcmpi(extension, "png") ||
101                   !strcmpi(extension, "ico"))
102                icon = pictureFile;
103             else if(!strcmpi(extension, "wav") || !strcmpi(extension, "mp3") ||
104                   !strcmpi(extension, "ogg") || !strcmpi(extension, "snd"))
105                icon = soundFile;
106             else if(!strcmpi(extension, "ear") || !strcmpi(extension, "7z") ||
107                   !strcmpi(extension, "rar") || !strcmpi(extension, "zip") ||
108                   !strcmpi(extension, "gz") || !strcmpi(extension, "bz2") ||
109                   !strcmpi(extension, "tar") || !strcmpi(extension, "arj") ||
110                   !strcmpi(extension, "lza") || !strcmpi(extension, "lzh") ||
111                   !strcmpi(extension, "cpio") || !strcmpi(extension, "z"))
112                icon = archiveFile;
113             else if(!strcmpi(extension, "cab") || !strcmpi(extension, "deb") ||
114                   !strcmpi(extension, "rpm"))
115                icon = packageFile;
116             else if(!strcmpi(extension, "iso") || !strcmpi(extension, "mds") ||
117                   !strcmpi(extension, "cue") || !strcmpi(extension, "bin") ||
118                   !strcmpi(extension, "ccd") || !strcmpi(extension, "bwt") ||
119                   !strcmpi(extension, "cdi") || !strcmpi(extension, "nrg"))
120                icon = opticalMediaImageFile;
121             else
122                icon = genFile;
123          }
124          else
125             icon = genFile;
126       }
127       else
128          icon = genFile; // tocheck: error icon?
129       return icon;
130    }
131
132    NodeIcons ::SelectNodeIcon(NodeTypes type)
133    {
134       switch(type)
135       {
136          case project:
137             return epjFile;
138          case file:
139             return genFile;
140          case folder:
141             return folder;
142          case resources:
143             return archiveFile;
144          case folderOpen:
145             return openFolder;
146       }
147       return genFile;
148    }
149 };
150
151 #define SELECTION_COLOR Color { 10, 36, 106 }
152
153 // On Windows & UNIX
154 #define SEPS    "/"
155 #define SEP     '/'
156
157 // this is so not working, why!
158 //struct TwoStrings
159 // return result was not even executed (did not step on while debugging)
160 class TwoStrings : struct
161 {
162    char * a;
163    char * b;
164
165    property bool
166    {
167       get
168       {
169          return a && a[0];
170       }
171    }
172
173    ~TwoStrings()
174    {
175       delete a;
176       delete b;
177    }
178 }
179
180 class DotMain : bool
181 {
182    //property char * { set { } }
183    DotMain ::FromFileName(const char * fileName)
184    {
185       DotMain dotMain = false;
186       if(fileName && fileName[0])
187       {
188          char ext[MAX_EXTENSION];
189          GetExtension(fileName, ext);
190          if(!strcmp(ext, "c") || !strcmp(ext, "ec"))
191          {
192             char stripExt[MAX_LOCATION];
193             strcpy(stripExt, fileName);
194             StripExtension(stripExt);
195             GetExtension(stripExt, ext);
196             if(!strcmp(ext, "main"))
197                dotMain = true;
198          }
199       }
200       return dotMain;
201    }
202 }
203
204 enum IntermediateFileType
205 {
206    none, ec, c, sym, imp, bowl, o;
207
208    //property char * { set { } }
209    IntermediateFileType ::FromExtension(char * extension)
210    {
211       IntermediateFileType type = none;
212       if(extension && extension[0])
213       {
214          if(!fstrcmp(extension, "ec"))
215             type = ec;
216          else if(!fstrcmp(extension, "c"))
217             type = c;
218          else if(!fstrcmp(extension, "sym"))
219             type = sym;
220          else if(!fstrcmp(extension, "imp"))
221             type = imp;
222          else if(!fstrcmp(extension, "bowl"))
223             type = bowl;
224          else if(!fstrcmp(extension, "o"))
225             type = o;
226       }
227       return type;
228    }
229 };
230
231 class ProjectNode : ListItem
232 {
233 public:
234    property String
235    {
236       set { return { fileName = value }; }
237       // TOCHECK: Is this isset necessary at all?
238       isset { return nodeType == file && !options && !configurations && !platforms && !files; }
239    };
240    property String folder
241    {
242       set
243       {
244          nodeType = folder;
245          if(strchr(value, '/'))
246          {
247             char p[MAX_LOCATION];
248             char n[MAX_FILENAME];
249             GetLastDirectory(value, n);
250             StripLastDirectory(value, p);
251             name = CopyString(n);
252             path = CopyString(p);
253          }
254          else
255             name = CopyString(value);
256       }
257       get
258       {
259          // TOCHECK: Non Reentrant
260          static char insidePath[MAX_LOCATION];
261
262          strcpy(insidePath, (parent.type == project) ? "" : parent.path);
263          PathCatSlash(insidePath, name);
264
265          if(!fstrcmp(path, insidePath))
266             return name;
267          else
268          {
269             strcpy(insidePath, path);
270             if(!insidePath[0]) strcpy(insidePath, ".");
271             PathCatSlash(insidePath, name);
272             return insidePath;
273          }
274       }
275       isset { return nodeType == folder; }
276    };
277    property const String fileName
278    {
279       set
280       {
281          nodeType = file;
282          if(strchr(value, '/'))
283          {
284             char p[MAX_LOCATION];
285             char n[MAX_FILENAME];
286             GetLastDirectory(value, n);
287             StripLastDirectory(value, p);
288             name = CopyValidateMakefilePath(n);
289             path = CopyValidateMakefilePath(p);
290          }
291          else
292             name = CopyValidateMakefilePath(value);
293       }
294       get
295       {
296          // TOCHECK: Non Reentrant
297          static char insidePath[MAX_LOCATION];
298
299          strcpy(insidePath, (parent.type == project) ? "" : parent.path);
300          if(!fstrcmp(path, insidePath))
301             return name;
302          else
303          {
304             strcpy(insidePath, path);
305             if(!insidePath[0]) strcpy(insidePath, ".");
306             PathCatSlash(insidePath, name);
307             return insidePath;
308          }
309       }
310       isset { return nodeType == file && (options || configurations || platforms); }
311    };
312
313    LinkList<ProjectNode> files;
314    property ProjectOptions options
315    {
316       get { return project ? project.options : options; }
317       set { if(project) { delete project.options; project.options = value; } else { delete options; options = value; } }
318       isset { ProjectOptions options = project ? project.options : this.options; return options && !options.isEmpty; }
319    }
320    property Array<PlatformOptions> platforms
321    {
322       get { return project ? project.platforms : platforms; }
323       set
324       {
325          if(project) { project.platforms = value; }
326          else
327          {
328             if(platforms) { platforms.Free(); delete platforms; }
329             if(value)
330             {
331                List<PlatformOptions> empty { };
332                Iterator<PlatformOptions> it { value };
333                platforms = value;
334                for(p : platforms; !p.options || p.options.isEmpty) empty.Add(p);
335                for(p : empty; it.Find(p)) platforms.Delete(it.pointer);
336                delete empty;
337             }
338          }
339       }
340       isset
341       {
342          Array<PlatformOptions> platforms = project ? project.platforms : this.platforms;
343          if(platforms)
344          {
345             for(p : platforms)
346             {
347                if(p.options && !p.options.isEmpty)
348                   return true;
349             }
350          }
351          return false;
352       }
353    }
354    property List<ProjectConfig> configurations
355    {
356       get { return project ? project.configurations : configurations; }
357       set
358       {
359          if(project)
360          {
361             if(project.configurations)
362             {
363                project.configurations.Free();
364                delete project.configurations;
365             }
366             project.configurations = value;
367          }
368          else
369          {
370             if(configurations) { configurations.Free(); delete configurations; }
371             if(value)
372             {
373                List<ProjectConfig> empty { };
374                Iterator<ProjectConfig> it { value };
375                configurations = value;
376                for(c : configurations)
377                {
378                   bool somethingSet = c.options && !c.options.isEmpty;
379                   // TODO: Implement isset keyword
380                   if(!somethingSet && c.platforms && c.platforms.count)
381                   {
382                      for(p : c.platforms)
383                      {
384                         if(p.options && !p.options.isEmpty)
385                         {
386                            somethingSet = true;
387                            break;
388                         }
389                      }
390                   }
391                   if(!somethingSet)
392                      empty.Add(c);
393                }
394                for(c : empty; it.Find(c)) configurations.Delete(it.pointer);
395                delete empty;
396             }
397          }
398       }
399       isset
400       {
401          if(!parent) return true;
402          if(configurations)
403          {
404             for(c : configurations)
405             {
406                bool somethingSet = c.options && !c.options.isEmpty;
407                if(!somethingSet && c.platforms && c.platforms.count)
408                {
409                   for(p : c.platforms)
410                   {
411                      if(p.options && !p.options.isEmpty)
412                      {
413                         somethingSet = true;
414                         break;
415                      }
416                   }
417                }
418                return somethingSet;
419             }
420          }
421          return false;
422       }
423    }
424
425 private:
426    ProjectOptions options;
427    Array<PlatformOptions> platforms;
428    List<ProjectConfig> configurations;
429    ProjectNodeType nodeType;
430    ProjectNode parent;
431    char * name;
432    char * info;
433
434    // This holds the absolute path of the .epj for the project topnode (without the filename)
435    // It holds a relative path to the topNode (project) for other nodes (folders and files)
436    // For folders, it includes the folder it refers to. If there is a name difference between the
437    // file system folder and the grouping folder of the project view, it maps to that folder.
438    char * path;
439
440    NodeTypes type;
441    NodeIcons icon;
442    int indent;
443    DataRow row;
444
445    bool modified;
446
447    // This is only set for Top Nodes
448    Project project;
449
450    void OpenRulesPlatformExclusionIfs(File f, int * ifCount, Array<Platform> platforms)
451    {
452       if(!platforms.Find(unknown))  // unknown is "Common"
453       {
454          // e.g. ifneq "$(or $(or $(OSX_TARGET),$(LINUX_TARGET)),$(WINDOWS_TARGET))"
455          int i = 0;
456          f.Puts("ifneq \"");
457          for(i = 0; platforms.count && i < platforms.count - 1; i++)
458             f.Puts("$(or ");
459          i = 0;
460          for(p : platforms)
461          {
462             if(i > 0)
463                f.Puts(",");
464             f.Puts("$(");
465             f.Puts(PlatformToMakefileTargetVariable(p));
466             f.Puts(")");
467             if(i > 0)
468                f.Puts(")");
469             i++;
470          }
471          f.Puts("\" \"\"\n");
472          (*ifCount)++;
473       }
474       else
475          *ifCount = 0;
476    }
477
478    ProjectConfig GetMatchingNodeConfig(ProjectConfig prjConfig)
479    {
480       ProjectConfig nodeConfig = null;
481       if(property::configurations && prjConfig)
482       {
483          const char * configName = prjConfig.name;
484          for(cfg : property::configurations)
485          {
486             if(!strcmpi(cfg.name, configName))
487             {
488                nodeConfig = cfg;
489                break;
490             }
491          }
492       }
493       return nodeConfig;
494    }
495
496    property ProjectNode root { get { ProjectNode n; for(n = this; n.parent; n = n.parent); return n; } }
497
498    property bool containsFile
499    {
500       get
501       {
502          bool result;
503          if(files)
504          {
505             for(child : files)
506             {
507                if(child.type == file ||
508                      ((child.type == folder || child.type == folderOpen) && child.containsFile))
509                {
510                   result = true;
511                   break;
512                }
513             }
514          }
515          else
516             result = false;
517          return result;
518       }
519    }
520
521    char * GetFullFilePath(char * buffer)
522    {
523       if(buffer)
524       {
525          strcpy(buffer, root.path);
526          PathCatSlash(buffer, path);
527          PathCatSlash(buffer, name);
528       }
529       return buffer;
530    }
531
532    char * GetObjectFileName(char * buffer, Map<String, NameCollisionInfo> namesInfo, IntermediateFileType type, bool dotMain)
533    {
534       if(buffer && (this.type == file || (this.type == project && dotMain == true)))
535       {
536          bool collision;
537          char extension[MAX_EXTENSION];
538          char moduleName[MAX_FILENAME];
539          NameCollisionInfo info;
540
541          GetExtension(name, extension);
542          ReplaceSpaces(moduleName, name);
543          StripExtension(moduleName);
544          info = namesInfo[moduleName];
545          collision = info ? info.IsExtensionColliding(extension) : false;
546
547          if(dotMain)
548          {
549             Project prj = property::project;
550             ReplaceSpaces(buffer, prj.moduleName);
551             StripExtension(buffer);
552             strcat(buffer, ".main.ec");
553          }
554          else
555             strcpy(buffer, name);
556          if(!strcmp(extension, "ec") || dotMain)
557          {
558             if(type == c)
559                ChangeExtension(buffer, "c", buffer);
560             else if(type == sym)
561                ChangeExtension(buffer, "sym", buffer);
562             else if(type == imp)
563                ChangeExtension(buffer, "imp", buffer);
564             else if(type == bowl)
565                ChangeExtension(buffer, "bowl", buffer);
566          }
567          if(type == o)
568          {
569             if(collision)
570                strcat(buffer, ".o");
571             else
572                ChangeExtension(buffer, "o", buffer);
573          }
574       }
575       return buffer;
576    }
577
578    char * GetFileSysMatchingPath(char * buffer)
579    {
580       if(buffer)
581       {
582          ProjectNode n, root = this.root;
583          for(n = this; n && (n.type == folder || n.type == project); n = n.parent)
584          {
585             strcpy(buffer, root.path);
586             if(n != root)
587                PathCatSlash(buffer, n.path);
588             if(FileExists(buffer).isDirectory)
589                break;
590          }
591          if(!(n && (n.type == folder || n.type == project)))
592             buffer[0] = '\0';
593       }
594       return buffer;
595    }
596
597    void CollectPerFileAndDirOptions(ProjectConfig prjConfig, Array<String> perFilePreprocessorDefs, Array<DirPath> perFileIncludeDirs)
598    {
599       ProjectNode node = null;
600       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
601       List<ProjectNode> nodeStack { };
602
603       for(node = this; node && node.parent; node = node.parent)
604          nodeStack.Add(node);
605
606       // Should we reverse this stack to give priority to the per-file includes? Does the following technique already reverse?
607
608       // TODO: Check how to fix duplication of following options when configuration is made per-config-per-file
609       while((node = nodeStack.lastIterator.data))
610       {
611          ProjectConfig config = GetMatchingNodeConfig(prjConfig);
612          ProjectOptions nodeOptions = node.property::options;
613          if(nodeOptions && nodeOptions.preprocessorDefinitions)
614          {
615             for(def : nodeOptions.preprocessorDefinitions)
616                perFilePreprocessorDefs.Add(CopyString(def));
617          }
618          if(config && config.options && config.options.preprocessorDefinitions)
619          {
620             for(def : config.options.preprocessorDefinitions)
621                perFilePreprocessorDefs.Add(CopyString(def));
622          }
623          if(nodeOptions && nodeOptions.includeDirs)
624          {
625             for(dir : nodeOptions.includeDirs)
626                perFileIncludeDirs.Add(CopySystemPath(dir));
627          }
628          if(config && config.options && config.options.includeDirs)
629          {
630             for(dir : config.options.includeDirs)
631                perFileIncludeDirs.Add(CopySystemPath(dir));
632          }
633          nodeStack.lastIterator.Remove();
634       }
635       delete nodeStack;
636    }
637
638
639    property Project project
640    {
641       get
642       {
643          ProjectNode n = this;
644          while(n && n.type != project) n = n.parent;
645          return n ? (*&n.project) : null;
646       }
647    }
648
649    void RenameConfig(const char * oldName, const char * newName)
650    {
651       if(files)
652       {
653          for(f : files; (f.configurations || f.files)) { f.RenameConfig(oldName, newName); }
654       }
655       if(property::configurations)
656       {
657          for(c : property::configurations; !strcmp(c.name, oldName))
658          {
659             delete c.name;
660             c.name = CopyString(newName);
661          }
662       }
663    }
664
665    void DeleteConfig(ProjectConfig configToDelete)
666    {
667       if(files)
668       {
669          for(f : files; (f.configurations || f.files)) { f.DeleteConfig(configToDelete); }
670       }
671       if(property::configurations)
672       {
673          Iterator<ProjectConfig> c { property::configurations };
674          while(c.Next())
675          {
676             ProjectConfig config = c.data;
677             if(!strcmp(configToDelete.name, config.name))
678             {
679                c.Remove();
680                delete config;
681                break;
682             }
683          }
684          if(!property::configurations.count)
685             property::configurations = null;
686       }
687    }
688
689    ProjectNode Backup()
690    {
691       ProjectNode backupNode { };
692
693       if(files)
694       {
695          backupNode.files = { };
696          for(f : files) backupNode.files.Add(f.Backup());
697       }
698       if(property::options)
699          backupNode.options = property::options.Copy();
700
701       if(property::platforms)
702       {
703          backupNode.platforms = { };
704          for(p : property::platforms)
705             backupNode.platforms.Add(p.Copy());
706       }
707
708       if(property::configurations)
709       {
710          backupNode.configurations = { };
711          for(c : property::configurations)
712             backupNode.configurations.Add(c.Copy());
713       }
714       return backupNode;
715    }
716
717    void Revert(ProjectNode backupNode)
718    {
719       if(files)
720       {
721          Iterator<ProjectNode> it { backupNode.files };
722          for(f : files)
723          {
724             it.Next();
725             f.Revert(it.data);
726          }
727       }
728
729       property::options = backupNode.options ? backupNode.options.Copy() : null;
730       if(backupNode.platforms)
731       {
732          Array<PlatformOptions> platforms { };
733          property::platforms = platforms;
734
735          for(p : backupNode.platforms)
736             platforms.Add(p.Copy());
737       }
738       if(backupNode.configurations)
739       {
740          List<ProjectConfig> configurations { };
741          property::configurations = configurations;
742          for(c : backupNode.configurations)
743             configurations.Add(c.Copy());
744       }
745    }
746
747    void FixupNode(char * parentPath)
748    {
749       if(!parent)
750       {
751          type = project;
752       }
753       else if(nodeType == file)
754       {
755          type = file;
756          if(!path)
757          {
758             path = CopyString((parent.type == folder || parent.type == resources) ? parentPath : "");
759          }
760       }
761       else if(nodeType == folder)
762       {
763          type = folder;
764
765          if(!path)
766          {
767             char temp[MAX_LOCATION];
768             strcpy(temp, (parent.type == folder || parent.type == resources) ? parentPath : "");
769             PathCatSlash(temp, name);
770             path = CopyString(temp);
771          }
772       }
773
774       indent = parent ? parent.indent + 1 : 0;
775
776       if(type == file)
777          icon = NodeIcons::SelectFileIcon(name);
778       else
779          icon = NodeIcons::SelectNodeIcon(type);
780
781       if(files)
782       {
783          for(f : files)
784          {
785             f.parent = this;
786
787             if(type == project)
788                parentPath[0] = '\0';
789             else if(type == resources || type == folder)
790                strcpy(parentPath, path);
791
792             f.FixupNode(parentPath);
793          }
794       }
795    }
796
797    const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
798    {
799       if(!needClass)
800       {
801          // TOCHECK: Called from JSON writer
802          if(nodeType == file && !property::options && !property::configurations && !property::platforms && name)
803          {
804             strcpy(tempString, "\"");
805             strcat(tempString, property::fileName);
806             strcat(tempString, "\"");
807             return tempString;
808          }
809          else
810             return null;
811       }
812       else
813          // TOCHECK: Called from ProjectView rendering
814          return name ? name : "";
815    }
816
817    ~ProjectNode()
818    {
819       if(files)
820       {
821          files.Free();
822          delete files;
823       }
824       if(!project)
825          delete options;
826
827       if(!project && platforms)
828       {
829          platforms.Free();
830          delete platforms;
831       };
832       if(!project && configurations)
833       {
834          configurations.Free();
835          delete configurations;
836       }
837
838       /////////////////////////////
839       delete path;
840       delete name;
841       delete info;
842    }
843
844    property bool isInResources
845    {
846       get
847       {
848          ProjectNode node;
849          for(node = this; node; node = node.parent)
850          {
851             if(node.type == resources)
852                return true;
853          }
854          return false;
855       }
856    }
857
858    TwoStrings GetPlatformSpecificFu(ProjectConfig prjConfig)
859    {
860       TwoStrings result { a = CopyString(""), b = CopyString("") };
861       // note: unknown platform is for common
862       Map<Platform, SetBool> exclusionInfo { };
863       MapNode<Platform, SetBool> mn;
864       char * exp;
865       const char * var;
866       int len;
867       SetBool common;
868
869       CollectExclusionInfo(exclusionInfo, prjConfig);
870       common = exclusionInfo[unknown];
871       {
872          Map<Platform, SetBool> cleaned { };
873          SetBool opposite = common == true ? false : true;
874          for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
875          {
876             if(mn.key == unknown || mn.value == opposite)
877               cleaned[mn.key] = mn.value;
878          }
879          delete exclusionInfo;
880          exclusionInfo = cleaned;
881       }
882
883       if(exclusionInfo.count > 1)
884       {
885          if(exclusionInfo.count > 2)
886          {
887             exp = result.a;
888             len = strlen(exp) + strlen("$(if $(or ");
889             exp = renew exp char[len+1];
890             strcat(exp, "$(if $(or ");
891             result.a = exp;
892
893             for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
894             {
895                if(mn.key != unknown)
896                {
897                   const char * comma = mn.next ? "," : "";
898
899                   var = PlatformToMakefileTargetVariable(mn.key);
900
901                   exp = result.a;
902                   len = strlen(exp) + strlen("$(") + strlen(var) + strlen(")") + strlen(comma);
903                   exp = renew exp char[len+1];
904                   strcat(exp, "$(");
905                   strcat(exp, var);
906                   strcat(exp, ")");
907                   strcat(exp, comma);
908                   result.a = exp;
909                }
910             }
911
912             exp = result.a;
913             len = strlen(exp) + strlen("),");
914             exp = renew exp char[len+1];
915          }
916          else
917          {
918             if(exclusionInfo.root.minimum.key != unknown)
919                var = PlatformToMakefileTargetVariable(exclusionInfo.root.minimum.key);
920             else
921                var = PlatformToMakefileTargetVariable(exclusionInfo.root.minimum.next.key);
922
923             exp = result.a;
924             len = strlen(exp) + strlen("$(if $(") + strlen(var) + strlen("),");
925             exp = renew exp char[len+1];
926             strcat(exp, "$(if $(");
927             strcat(exp, var);
928          }
929
930          strcat(exp, "),");
931          result.a = exp;
932
933          exp = common == true ? result.b : result.a;
934          len = strlen(exp) + strlen(",");
935          exp = renew exp char[len+1];
936          strcat(exp, ",");
937          if(common == true) result.b = exp; else result.a = exp;
938
939          exp = result.b;
940          len = strlen(exp) + strlen(")");
941          exp = renew exp char[len+1];
942          strcat(exp, ")");
943          result.b = exp;
944       }
945       delete exclusionInfo;
946
947       return result;
948    }
949
950    bool GetIsExcluded(ProjectConfig prjConfig)
951    {
952       bool result;
953       // note: unknown platform is for common
954       Map<Platform, SetBool> exclusionInfo { };
955       CollectExclusionInfo(exclusionInfo, prjConfig);
956       if(exclusionInfo.count == 0)
957          result = false;
958       else if(exclusionInfo.count == 1)
959          result = exclusionInfo.root.minimum.value == true;
960       else
961       {
962          SetBool check = exclusionInfo.root.minimum.value;
963          MapNode<Platform, SetBool> mn;
964          for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
965          {
966             if(check != mn.value)
967                break;
968          }
969          if(!mn) // all are same
970             result = check == true;
971          else
972             result = false;
973       }
974       delete exclusionInfo;
975       return result;
976    }
977
978    void CollectExclusionInfo(Map<Platform, SetBool> output, ProjectConfig prjConfig)
979    {
980       // note: unknown platform is for common
981       Platform platform;
982       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
983       ProjectOptions options = property::options;
984       Array<PlatformOptions> platforms = property::platforms;
985       List<ProjectConfig> configurations = property::configurations;
986
987       if(parent)
988          parent.CollectExclusionInfo(output, prjConfig);
989       else
990          output[unknown] = unset;
991
992       if(options && options.excludeFromBuild)
993          output[unknown] = options.excludeFromBuild;
994
995       if(config && config.options && config.options.excludeFromBuild)
996          output[unknown] = config.options.excludeFromBuild;
997
998       if(platforms)
999       {
1000          for(p : platforms)
1001          {
1002             if(p.options.excludeFromBuild && (platform = p.name))
1003                output[platform] = p.options.excludeFromBuild;
1004          }
1005       }
1006       if(config && config.platforms)
1007       {
1008          for(p : config.platforms)
1009          {
1010             if(p.options.excludeFromBuild && (platform = p.name))
1011                output[platform] = p.options.excludeFromBuild;
1012          }
1013       }
1014    }
1015
1016    void EnsureVisible()
1017    {
1018       if(parent)
1019          parent.EnsureVisible();
1020       row.collapsed = false;
1021    }
1022
1023    void Delete()
1024    {
1025       if(parent)
1026          parent.files.Delete(this);
1027    }
1028
1029    ProjectNode Find(const char * name, bool includeResources)
1030    {
1031       ProjectNode result = null;
1032       if(files)
1033       {
1034          for(child : files)
1035          {
1036             if(includeResources || child.type != resources)
1037             {
1038                if(child.type != folder && child.name && !strcmpi(child.name, name))
1039                {
1040                   result = child;
1041                   break;
1042                }
1043                result = child.Find(name, includeResources);
1044                if(result)
1045                   break;
1046             }
1047          }
1048       }
1049       return result;
1050    }
1051
1052    ProjectNode FindWithPath(const char * name, bool includeResources)
1053    {
1054       ProjectNode result = null;
1055       if(files)
1056       {
1057          for(child : files)
1058          {
1059             if(includeResources || child.type != resources)
1060             {
1061                char path[MAX_LOCATION];
1062                strcpy(path, child.path);
1063                if(child.type != folder && child.name)
1064                {
1065                   PathCatSlash(path, child.name);
1066                   if(!strcmpi(path, name))
1067                   {
1068                      result = child;
1069                      break;
1070                   }
1071                }
1072                result = child.FindWithPath(name, includeResources);
1073                if(result)
1074                   break;
1075             }
1076          }
1077       }
1078       return result;
1079    }
1080
1081    ProjectNode FindByFullPath(const char * path, bool includeResources)
1082    {
1083       if(files)
1084       {
1085          char name[MAX_FILENAME];
1086          GetLastDirectory(path, name);
1087          return InternalFindByFullPath(path, includeResources, name);
1088       }
1089       return null;
1090    }
1091
1092    ProjectNode InternalFindByFullPath(const char * path, bool includeResources, const char * lastDirName)
1093    {
1094       ProjectNode result = null;
1095       if(files)
1096       {
1097          for(child : files)
1098          {
1099             if(includeResources || child.type != resources)
1100             {
1101                if(child.type != file)
1102                   result = child.InternalFindByFullPath(path, includeResources, lastDirName);
1103                else if(child.name && !fstrcmp(lastDirName, child.name))
1104                {
1105                   char p[MAX_LOCATION];
1106                   child.GetFullFilePath(p);
1107                   if(!fstrcmp(p, path))
1108                   {
1109                      result = child;
1110                      break;
1111                   }
1112                }
1113                if(result)
1114                   break;
1115             }
1116          }
1117       }
1118       return result;
1119    }
1120
1121    ProjectNode FindByObjectFileName(const char * fileName, IntermediateFileType type, bool dotMain, Map<String, NameCollisionInfo> namesInfo)
1122    {
1123       char p[MAX_LOCATION];
1124       ProjectNode result = null;
1125       if(dotMain == true && this.type == project)
1126       {
1127          GetObjectFileName(p, namesInfo, type, dotMain);
1128          if(!fstrcmp(p, fileName))
1129             result = this;
1130       }
1131       else if(files)
1132       {
1133          for(child : files; child.type != resources)
1134          {
1135             if(child.type != file && (result = child.FindByObjectFileName(fileName, type, dotMain, namesInfo)))
1136                break;
1137             else if(child.type == file && child.name)
1138             {
1139                child.GetObjectFileName(p, namesInfo, type, dotMain);
1140                if(!fstrcmp(p, fileName))
1141                {
1142                   result = child;
1143                   break;
1144                }
1145             }
1146          }
1147       }
1148       return result;
1149    }
1150
1151    ProjectNode FindSpecial(const char * name, bool recursive, bool includeResources, bool includeFolders)
1152    {
1153       ProjectNode result = null;
1154       if(files)
1155       {
1156          for(child : files)
1157          {
1158             if(includeResources || child.type != resources)
1159             {
1160                if((includeFolders || child.type != folder) && child.name && !strcmpi(child.name, name))
1161                {
1162                   result = child;
1163                   break;
1164                }
1165                if(recursive)
1166                   result = child.FindSpecial(name, recursive, includeResources, includeFolders);
1167                if(result)
1168                   break;
1169             }
1170          }
1171       }
1172       return result;
1173    }
1174
1175    ProjectNode FindSameNameConflict(const char * name, bool includeResources,
1176       Map<Platform, SetBool> exclusionInfo, ProjectConfig prjConfig)
1177    {
1178       ProjectNode result = null;
1179       Map<Platform, SetBool> compareExclusion { };
1180       SetBool common, commonComp;
1181       SetBool actual, actualComp;
1182       if(files)
1183       {
1184          for(child : files)
1185          {
1186             if(includeResources || child.type != resources)
1187             {
1188                if(child.type != folder && child.name && !strcmpi(child.name, name))
1189                {
1190                   child.CollectExclusionInfo(compareExclusion, prjConfig);
1191                   common = exclusionInfo[unknown];
1192                   commonComp = compareExclusion[unknown];
1193                   if(exclusionInfo.count == 1 && compareExclusion.count == 1)
1194                   {
1195                      if(!(common == true || commonComp == true))
1196                      {
1197                         result = child;
1198                         break;
1199                      }
1200                   }
1201                   else
1202                   {
1203                      Platform platform;
1204                      for(platform = (Platform)1; platform < Platform::enumSize; platform++)
1205                      {
1206                         actual = common;
1207                         actualComp = commonComp;
1208                         if(exclusionInfo[platform] != unset)
1209                            actual = exclusionInfo[platform];
1210                         if(compareExclusion[platform] != unset)
1211                            actualComp = compareExclusion[platform];
1212                         if(!(actual == true || actualComp == true))
1213                         {
1214                            result = child;
1215                            break;
1216                         }
1217                      }
1218                      if(result) break;
1219                   }
1220                   compareExclusion.Free();
1221                   break;
1222                }
1223                result = child.FindSameNameConflict(name, includeResources, exclusionInfo, prjConfig);
1224                if(result) break;
1225             }
1226          }
1227          compareExclusion.Free();
1228       }
1229       delete compareExclusion;
1230       return result;
1231    }
1232
1233    ProjectNode Add(Project project, const char * filePath, ProjectNode after, NodeTypes type, NodeIcons icon, bool checkIfExists)
1234    {
1235       ProjectNode node = null;
1236       if(!project.topNode.FindByFullPath(filePath, true))
1237       {
1238          char temp[MAX_LOCATION];
1239          Map<Platform, SetBool> exclusionInfo { };
1240
1241          GetLastDirectory(filePath, temp);
1242          //if(!checkIfExists || !project.topNode.Find(temp, false))
1243
1244          // TOCHECK: Shouldn't this apply either for all configs or none?
1245          CollectExclusionInfo(exclusionInfo, project.config);
1246          if(!checkIfExists || type == folder || !project.topNode.FindSameNameConflict(temp, false, exclusionInfo, project.config))
1247          {
1248             // Do the check for folder in the same parent or resource files only here
1249             if(type == folder || !checkIfExists)
1250             {
1251                for(node : files)
1252                {
1253                   if(node.name && !strcmpi(node.name, temp))
1254                      return null;
1255                }
1256             }
1257
1258             node = ProjectNode { parent = this, indent = indent + 1, type = type, icon = icon, name = CopyString(temp) };
1259             if(type != file)
1260             {
1261                node.files = { };
1262                node.nodeType = folder;
1263             }
1264             if(type != folder)
1265             {
1266                if(filePath)
1267                {
1268                   StripLastDirectory(filePath, temp);
1269                   MakePathRelative(temp, project.topNode.path, temp);
1270                   node.path = CopyUnixPath(temp);
1271                }
1272                node.nodeType = file;
1273             }
1274             else
1275             {
1276                strcpy(temp, (type == NodeTypes::project) ? "" : path);
1277                PathCatSlash(temp, node.name);
1278                node.path = CopyString(temp);
1279             }
1280             files.Insert(after, node);
1281          }
1282          delete exclusionInfo;
1283       }
1284       return node;
1285    }
1286
1287 #ifndef MAKEFILE_GENERATOR
1288    void OnDisplay(Surface surface, int x, int y, int width, ProjectView projectView, Alignment alignment, DataDisplayFlags displayFlags)
1289    {
1290       char label[MAX_FILENAME];
1291       int indent = 16;
1292       int xStart;
1293       int len;
1294       int w, h;
1295       Bitmap bmp;
1296       bool showConfig = true;
1297
1298       if(!projectView)
1299       {
1300          showConfig = false;
1301          projectView = ide.projectView;
1302       }
1303
1304       bmp = projectView.icons[icon].bitmap;
1305       xStart = /*indent * indent + */x + (bmp ? (bmp.width + 5) : 0);
1306
1307       GetLastDirectory(name, label);
1308       if(!showConfig || projectView.drawingInProjectSettingsDialogHeader)
1309       {
1310          if(projectView.drawingInProjectSettingsDialogHeader || (type == project && info))
1311          {
1312             if(projectView.projectSettingsDialog && projectView.projectSettingsDialog.buildTab)
1313             {
1314                const char * addendum = projectView.projectSettingsDialog.buildTab.selectedConfigName;
1315                if(strlen(addendum))
1316                {
1317                   strcat(label, " (");
1318                   strcat(label, addendum);
1319                   strcat(label, ")");
1320                }
1321                addendum = projectView.projectSettingsDialog.buildTab.selectedPlatformName;
1322                if(strlen(addendum))
1323                {
1324                   strcat(label, " (");
1325                   strcat(label, addendum);
1326                   strcat(label, ")");
1327                }
1328             }
1329          }
1330       }
1331       else if(!projectView.drawingInProjectSettingsDialog)
1332       {
1333          if(modified)
1334             strcat(label, " *");
1335          if(type == project && info)
1336          {
1337             int len = strlen(info) + 4;
1338             char * more = new char[len];
1339             sprintf(more, " (%s)", info);
1340             strcat(label, more);
1341             delete more;
1342          }
1343       }
1344       len = strlen(label);
1345
1346       if(!bmp)
1347       {
1348          if(type == folder || type == folderOpen)
1349             surface.SetForeground(yellow);
1350          indent = 8;
1351       }
1352
1353       surface.TextOpacity(false);
1354       surface.TextExtent(label, len, &w, &h);
1355       h = Max(h, 16);
1356
1357       // Draw the current row stipple
1358       if(displayFlags.selected)
1359          //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
1360          //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
1361          surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
1362
1363       surface.WriteTextDots(alignment, xStart, y + 2, width, label, len);
1364
1365       if(!app.textMode)
1366       {
1367          if(displayFlags.current)
1368          {
1369             if(displayFlags.active)
1370             {
1371                surface.LineStipple(0x5555);
1372                if(displayFlags.selected)
1373                   surface.SetForeground(projectView.fileList.stippleColor);
1374                else
1375                   surface.SetForeground(projectView.fileList.foreground);
1376             }
1377             else
1378             {
1379                surface.SetForeground(SELECTION_COLOR);
1380             }
1381             surface.Rectangle(xStart - 3, y, xStart + w + 1, y + h - 1);
1382             surface.LineStipple(0);
1383          }
1384
1385          if(bmp)
1386          {
1387             surface.SetForeground(white);
1388             surface.Blit(bmp, x /*+ indent * indent*/,y,0,0, bmp.width, bmp.height);
1389          }
1390       }
1391    }
1392 #endif
1393
1394    int OnCompare(ProjectNode b)
1395    {
1396       int result;
1397       if(type == b.type /*|| type >= TYPE_DRIVE*/)
1398          result = strcmpi(name, b.name);
1399       else
1400       {
1401          if(type == folder && b.type == file) result = -1;
1402          else if(type == file && b.type == folder) result = 1;
1403       }
1404       return result;
1405    }
1406
1407    bool ContainsFilesWithExtension(const char * extension, ProjectConfig prjConfig)
1408    {
1409       if(type == file)
1410       {
1411          char ext[MAX_EXTENSION];
1412          GetExtension(name, ext);
1413          if(!fstrcmp(ext, extension))
1414             return true;
1415       }
1416       else if(files)
1417       {
1418          for(child : files)
1419          {
1420             if(child.type != resources && (child.type == folder || !prjConfig || !child.GetIsExcluded(prjConfig)))
1421             {
1422                if(child.ContainsFilesWithExtension(extension, prjConfig))
1423                   return true;
1424             }
1425          }
1426       }
1427       return false;
1428    }
1429
1430    void GenMakefileGetNameCollisionInfo(Map<String, NameCollisionInfo> namesInfo, ProjectConfig prjConfig)
1431    {
1432       if(type == file)
1433       {
1434          char extension[MAX_EXTENSION];
1435          GetExtension(name, extension);
1436          if(!strcmpi(extension, "ec") || !strcmpi(extension, "s") || !strcmpi(extension, "c") ||
1437                !strcmpi(extension, "rc") || !strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
1438                !strcmpi(extension, "cxx") || !strcmpi(extension, "m") || !strcmpi(extension, "mm"))
1439          {
1440             char moduleName[MAX_FILENAME];
1441             NameCollisionInfo info;
1442             ReplaceSpaces(moduleName, name);
1443             StripExtension(moduleName);
1444             info = namesInfo[moduleName];
1445             if(!info)
1446                info = NameCollisionInfo { };
1447             info.count++; // += 1; unless this is for a bug?
1448             if(!strcmpi(extension, "ec"))
1449                info.ec = true;
1450             else if(!strcmpi(extension, "s"))
1451                info.s = true;
1452             else if(!strcmpi(extension, "c"))
1453                info.c = true;
1454             else if(!strcmpi(extension, "rc"))
1455                info.rc = true;
1456             else if(!strcmpi(extension, "cpp"))
1457                info.cpp = true;
1458             else if(!strcmpi(extension, "cc"))
1459                info.cc = true;
1460             else if(!strcmpi(extension, "cxx"))
1461                info.cxx = true;
1462             else if(!strcmpi(extension, "m"))
1463                info.m = true;
1464             else if(!strcmpi(extension, "mm"))
1465                info.mm = true;
1466             namesInfo[moduleName] = info;
1467          }
1468       }
1469       else if(files)
1470       {
1471          for(child : files)
1472          {
1473             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1474                child.GenMakefileGetNameCollisionInfo(namesInfo, prjConfig);
1475          }
1476       }
1477    }
1478
1479    int GenMakefilePrintNode(File f, Project project, GenMakefilePrintTypes printType,
1480       Map<String, NameCollisionInfo> namesInfo, Array<String> items,
1481       ProjectConfig prjConfig, bool * containsCXX)
1482    {
1483       int count = 0;
1484       if(type == file)
1485       {
1486          char s[2048];
1487          TwoStrings ts = GetPlatformSpecificFu(prjConfig);
1488          char moduleName[MAX_FILENAME];
1489          char extension[MAX_EXTENSION];
1490          GetExtension(name, extension);
1491          if(printType == resources)
1492          {
1493             bool useRes;
1494             char tempPath[MAX_LOCATION];
1495             char modulePath[MAX_LOCATION];
1496
1497             tempPath[0] = '\0';
1498             if(eString_PathInsideOfMore(path, project.resNode.path, tempPath))
1499             {
1500                useRes = true;
1501                PathCatSlash(tempPath, name);
1502             }
1503             else
1504             {
1505                useRes = false;
1506                strcpy(tempPath, path);
1507                PathCatSlash(tempPath, name);
1508             }
1509             EscapeForMake(modulePath, tempPath, false, true, false);
1510             sprintf(s, "%s%s%s%s", ts.a, useRes ? "$(RES)" : "", modulePath, ts.b);
1511             items.Add(CopyString(s));
1512          }
1513          else if(printType == sources)
1514          {
1515             if(!strcmpi(extension, "s") || !strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1516                   !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1517                   !strcmpi(extension, "m") || !strcmpi(extension, "mm"))
1518             {
1519                char modulePath[MAX_LOCATION];
1520                EscapeForMake(modulePath, path, false, true, false);
1521                EscapeForMake(moduleName, name, false, true, false);
1522                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
1523                items.Add(CopyString(s));
1524             }
1525          }
1526          else if(printType == eCsources)
1527          {
1528             if(!strcmpi(extension, "ec"))
1529             {
1530                char modulePath[MAX_LOCATION];
1531                EscapeForMake(modulePath, path, true, true, false);
1532                EscapeForMake(moduleName, name, true, true, false);
1533                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
1534                items.Add(CopyString(s));
1535                count++;
1536             }
1537          }
1538          else if(printType == rcSources)
1539          {
1540             if(!strcmpi(extension, "rc"))
1541             {
1542                char modulePath[MAX_LOCATION];
1543                EscapeForMake(modulePath, path, false, true, false);
1544                EscapeForMake(moduleName, name, false, true, false);
1545                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
1546                items.Add(CopyString(s));
1547                count++;
1548             }
1549          }
1550          else if(!strcmpi(extension, "s") || !strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1551                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1552                !strcmpi(extension, "m") || !strcmpi(extension, "mm"))
1553          {
1554             if(printType == objects)
1555             {
1556                bool collision;
1557                NameCollisionInfo info;
1558                count++;
1559                EscapeForMake(moduleName, name, false, true, false);
1560                StripExtension(moduleName);
1561                info = namesInfo[moduleName];
1562                collision = info ? info.IsExtensionColliding(extension) : false;
1563                sprintf(s, "%s$(OBJ)%s%s%s.o%s", ts.a, moduleName, collision ? "." : "", collision ? extension : "", ts.b);
1564                items.Add(CopyString(s));
1565                if(containsCXX && (!strcmpi(extension, "cpp") || !strcmpi(extension, "cc") || !strcmpi(extension, "cxx")))
1566                   *containsCXX = true;
1567             }
1568          }
1569          delete ts;
1570       }
1571       else if(files)
1572       {
1573          for(child : files)
1574          {
1575             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1576                count += child.GenMakefilePrintNode(f, project, printType, namesInfo, items, prjConfig, containsCXX);
1577          }
1578       }
1579       return count;
1580    }
1581
1582    void GenMakefilePrintSymbolRules(File f, Project project,
1583          ProjectConfig prjConfig, //Map<Platform, bool> parentExcludedPlatforms,
1584          Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
1585    {
1586       int ifCount = 0;
1587       Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
1588       //ProjectNode child;
1589       //char objDir[MAX_LOCATION];
1590       //ReplaceSpaces(objDir, config.objDir.dir);
1591
1592       //eSystem_Log("Printing Symbol Rules\n");
1593       if(type == file)
1594       {
1595          char extension[MAX_EXTENSION];
1596          char modulePath[MAX_LOCATION];
1597          char moduleName[MAX_FILENAME];
1598
1599          GetExtension(name, extension);
1600          if(!strcmpi(extension, "ec"))
1601          {
1602             DualPipe dep;
1603             char command[2048];
1604
1605             ReplaceSpaces(moduleName, name);
1606             StripExtension(moduleName);
1607
1608             ReplaceSpaces(modulePath, path);
1609             if(modulePath[0]) strcat(modulePath, SEPS);
1610
1611             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
1612 #if 0
1613             // *** Dependency command ***
1614             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s", moduleName,
1615                modulePath, moduleName, extension);
1616
1617             // System Includes (from global settings)
1618             for(item : compiler.dirs[Includes])
1619             {
1620                strcat(command, " -isystem ");
1621                if(strchr(item, ' '))
1622                {
1623                   strcat(command, "\"");
1624                   strcat(command, item);
1625                   strcat(command, "\"");
1626                }
1627                else
1628                   strcat(command, item);
1629             }
1630
1631             for(item : project.includeDirs)
1632             {
1633                strcat(command, " -I");
1634                if(strchr(item, ' '))
1635                {
1636                   strcat(command, "\"");
1637                   strcat(command, item);
1638                   strcat(command, "\"");
1639                }
1640                else
1641                   strcat(command, item);
1642             }
1643             for(item : project.preprocessorDefs)
1644             {
1645                strcat(command, " -D");
1646                strcat(command, item);
1647             }
1648
1649             // Execute it
1650             if((dep = DualPipeOpen(PipeOpenMode { output = true, error = true/*, input = true*/ }, command)))
1651             {
1652                char line[1024];
1653                bool firstLine = true;
1654                bool result = true;
1655
1656                // To do some time: auto save external dependencies?
1657                while(!dep.Eof())
1658                {
1659                   if(dep.GetLine(line, sizeof(line)-1))
1660                   {
1661                      if(firstLine)
1662                      {
1663                         char * colon = strstr(line, ":");
1664                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1665                         {
1666                            result = false;
1667                            break;
1668                         }
1669                         firstLine = false;
1670                      }
1671                      f.Puts(line);
1672                      f.Puts("\n");
1673                   }
1674                   if(!result) break;
1675                }
1676                delete dep;
1677
1678                // If we failed to generate dependencies...
1679                if(!result)
1680                {
1681 #endif
1682                   f.Printf("$(OBJ)%s.sym: %s%s.%s\n",
1683                      moduleName, modulePath, moduleName, extension);
1684
1685                   f.Puts("\t$(ECP)");
1686
1687                   f.Puts(" $(CFLAGS)");
1688                   f.Puts(" $(CECFLAGS)"); // tocheck: what of this? should this stuff be per-file customized?
1689
1690                   GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
1691                   GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
1692
1693                   f.Printf(" -c $(call quote_path,%s%s.%s) -o $(call quote_path,$@)\n",
1694                      modulePath, moduleName, extension);
1695                   if(ifCount) f.Puts("endif\n");
1696                   f.Puts("\n");
1697 #if 0
1698                }
1699             }
1700 #endif
1701          }
1702       }
1703       if(files)
1704       {
1705          if(ContainsFilesWithExtension("ec", prjConfig))
1706          {
1707             for(child : files)
1708             {
1709                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1710                   child.GenMakefilePrintSymbolRules(f, project, prjConfig, /*excludedPlatforms,*/
1711                         nodeCFlagsMapping, nodeECFlagsMapping);
1712             }
1713          }
1714       }
1715       delete platforms;
1716    }
1717
1718    void GenMakefilePrintPrepecsRules(File f, Project project,
1719          ProjectConfig prjConfig, /*Map<Platform, bool> parentExcludedPlatforms,*/
1720          Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
1721    {
1722       int ifCount = 0;
1723       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1724       Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
1725       //ProjectNode child;
1726       //char objDir[MAX_LOCATION];
1727       //ReplaceSpaces(objDir, config.objDir.dir);
1728
1729       //eSystem_Log("Printing Symbol Rules\n");
1730       if(type == file)
1731       {
1732          char extension[MAX_EXTENSION];
1733          char modulePath[MAX_LOCATION];
1734          char moduleName[MAX_FILENAME];
1735
1736          GetExtension(name, extension);
1737          if(!strcmpi(extension, "ec"))
1738          {
1739             DualPipe dep;
1740             char command[2048];
1741
1742             ReplaceSpaces(moduleName, name);
1743             StripExtension(moduleName);
1744
1745             ReplaceSpaces(modulePath, path);
1746             if(modulePath[0]) strcat(modulePath, SEPS);
1747
1748             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
1749             f.Printf("$(OBJ)%s$(EC): %s%s.%s\n",
1750                moduleName, modulePath, moduleName, extension);
1751             /*f.Printf("\t$(CPP) %s%s.%s %s$(S)\n\n",
1752                modulePath, moduleName, extension, moduleName);*/
1753
1754             f.Puts("\t$(CPP)");
1755
1756             f.Puts(" $(CFLAGS)");
1757             //f.Puts(" $(CECFLAGS)");
1758             //GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
1759             GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
1760
1761             f.Printf(" -x c -E %s%s.%s -o $(OBJ)%s$(EC)\n",
1762                modulePath, moduleName, extension, moduleName);
1763             if(ifCount) f.Puts("endif\n");
1764             f.Puts("\n");
1765          }
1766       }
1767       if(files)
1768       {
1769          if(ContainsFilesWithExtension("ec", prjConfig))
1770          {
1771             for(child : files)
1772             {
1773                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1774                   child.GenMakefilePrintPrepecsRules(f, project, prjConfig, /*excludedPlatforms,*/
1775                         nodeCFlagsMapping, nodeECFlagsMapping);
1776             }
1777          }
1778       }
1779       delete platforms;
1780    }
1781
1782    void GenMakefilePrintCObjectRules(File f, Project project,
1783       ProjectConfig prjConfig, /*Map<Platform, bool> parentExcludedPlatforms,*/
1784       Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
1785    {
1786       int ifCount = 0;
1787       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1788       Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
1789       //ProjectNode child;
1790       //char objDir[MAX_LOCATION];
1791       //ReplaceSpaces(objDir, config.objDir.dir);
1792       //eSystem_Log("Printing C Object Rules\n");
1793       if(type == file)
1794       {
1795          char extension[MAX_EXTENSION];
1796          char modulePath[MAX_LOCATION];
1797          char moduleName[MAX_FILENAME];
1798
1799          GetExtension(name, extension);
1800          if(!strcmpi(extension, "ec"))
1801          {
1802             DualPipe dep;
1803             char command[2048];
1804
1805             ReplaceSpaces(moduleName, name);
1806             StripExtension(moduleName);
1807
1808             ReplaceSpaces(modulePath, path);
1809             if(modulePath[0]) strcat(modulePath, SEPS);
1810
1811             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
1812 #if 0
1813             // *** Dependency command ***
1814             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s",
1815                moduleName, modulePath, moduleName, extension);
1816
1817             // System Includes (from global settings)
1818             for(item : compiler.dirs[Includes])
1819             {
1820                strcat(command, " -isystem ");
1821                if(strchr(item, ' '))
1822                {
1823                   strcat(command, "\"");
1824                   strcat(command, item);
1825                   strcat(command, "\"");
1826                }
1827                else
1828                   strcat(command, item);
1829             }
1830
1831             for(item : config.includeDirs)
1832             {
1833                strcat(command, " -I");
1834                if(strchr(item, ' '))
1835                {
1836                   strcat(command, "\"");
1837                   strcat(command, item);
1838                   strcat(command, "\"");
1839                }
1840                else
1841                   strcat(command, item);
1842             }
1843             for(item : config.preprocessorDefs)
1844             {
1845                strcat(command, " -D");
1846                strcat(command, item);
1847             }
1848
1849             // Execute it
1850             if((dep = DualPipeOpen(PipeOpenMode { output = true, error = true/*, input = true*/ }, command)))
1851             {
1852                char line[1024];
1853                bool result = true;
1854                bool firstLine = true;
1855
1856                // To do some time: auto save external dependencies?
1857                while(!dep.Eof())
1858                {
1859                   if(dep.GetLine(line, sizeof(line)-1))
1860                   {
1861                      if(firstLine)
1862                      {
1863                         char * colon = strstr(line, ":");
1864                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1865                         {
1866                            result = false;
1867                            break;
1868                         }
1869                         firstLine = false;
1870                      }
1871                      f.Puts(line);
1872                      f.Puts("\n");
1873                   }
1874                   if(!result) break;
1875                }
1876                delete dep;
1877
1878                // If we failed to generate dependencies...
1879                if(!result)
1880                {
1881                   /* COMMENTED OUT FOR NOW
1882                   f.Printf("$(OBJ)%s.c: %s%s.%s $(Symbols)\n",
1883                      moduleName, modulePath, moduleName, extension);
1884                   */
1885 #endif
1886                   f.Printf("$(OBJ)%s.c: %s%s.%s $(OBJ)%s.sym | $(SYMBOLS)\n",
1887                      moduleName, modulePath, moduleName, extension, moduleName);
1888 #if 0
1889                }
1890             }
1891 #endif
1892          /*
1893             f.Printf("\t$(ECC) %s%s.%s $(OBJ)%s.c\n\n",
1894                modulePath, moduleName, extension, moduleName);
1895          */
1896
1897             f.Puts("\t$(ECC)");
1898
1899             f.Puts(" $(CFLAGS)");
1900             f.Puts(" $(CECFLAGS)"); // what of this? should this stuff be per-file customized?
1901             GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
1902             GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
1903             f.Puts(" $(FVISIBILITY)");
1904
1905             f.Printf(" -c $(call quote_path,%s%s.%s) -o $(call quote_path,$@) -symbols $(OBJ)\n",
1906                modulePath, moduleName, extension);
1907             if(ifCount) f.Puts("endif\n");
1908             f.Puts("\n");
1909          }
1910       }
1911       if(files)
1912       {
1913          if(ContainsFilesWithExtension("ec", prjConfig))
1914          {
1915             for(child : files)
1916             {
1917                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1918                   child.GenMakefilePrintCObjectRules(f, project, prjConfig, /*excludedPlatforms,*/
1919                         nodeCFlagsMapping, nodeECFlagsMapping);
1920             }
1921          }
1922       }
1923       delete platforms;
1924    }
1925
1926    void GenMakefilePrintObjectRules(File f, Project project,
1927       Map<String, NameCollisionInfo> namesInfo,
1928       ProjectConfig prjConfig,
1929       //Map<Platform, bool> parentExcludedPlatforms,
1930       Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
1931    {
1932       int ifCount = 0;
1933       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1934       Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
1935       //ProjectNode child;
1936       //char objDir[MAX_LOCATION];
1937       //ReplaceSpaces(objDir, config.objDir.dir);
1938       //eSystem_Log("Printing Object Rules\n");
1939       if(type == file)
1940       {
1941          bool collision;
1942          char extension[MAX_EXTENSION];
1943          char modulePath[MAX_LOCATION];
1944          char moduleName[MAX_FILENAME];
1945
1946          GetExtension(name, extension);
1947          if(!strcmpi(extension, "s") || !strcmpi(extension, "c") || !strcmpi(extension, "rc") ||
1948                !strcmpi(extension, "cpp") || !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1949                !strcmpi(extension, "m") || !strcmpi(extension, "mm") || !strcmpi(extension, "ec"))
1950          {
1951             DualPipe dep;
1952             char command[2048];
1953             NameCollisionInfo info;
1954
1955             ReplaceSpaces(moduleName, name);
1956             StripExtension(moduleName);
1957
1958             info = namesInfo[moduleName];
1959             collision = info ? info.IsExtensionColliding(extension) : false;
1960
1961             ReplaceSpaces(modulePath, path);
1962             if(modulePath[0]) strcat(modulePath, SEPS);
1963
1964             /*
1965 #if 0
1966             // *** Dependency command ***
1967             if(!strcmpi(extension, "ec"))
1968                sprintf(command, "%s -MT $(OBJ)%s.o -MM $(OBJ)%s.c", "$(CPP)", moduleName, moduleName);
1969             else
1970                sprintf(command, "%s -MT $(OBJ)%s.o -MM %s%s.%s", (!strcmpi(extension, "cc") || !strcmpi(extension, "cxx") || !strcmpi(extension, "cpp")) ? "$(CXX)" : "$(CC)",
1971                   moduleName, modulePath, moduleName, extension);
1972
1973             if(!strcmpi(extension, "ec"))
1974             {
1975                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1976             }
1977             else
1978             {
1979                // System Includes (from global settings)
1980                for(item : compiler.dirs[includes])
1981                {
1982                   strcat(command, " -isystem ");
1983                   if(strchr(item, ' '))
1984                   {
1985                      strcat(command, "\"");
1986                      strcat(command, item);
1987                      strcat(command, "\"");
1988                   }
1989                   else
1990                      strcat(command, item);
1991                }
1992
1993                for(item : config.includeDirs)
1994                {
1995                   strcat(command, " -I");
1996                   if(strchr(item, ' '))
1997                   {
1998                      strcat(command, "\"");
1999                      strcat(command, item);
2000                      strcat(command, "\"");
2001                   }
2002                   else
2003                      strcat(command, item);
2004                }
2005                for(item : config.preprocessorDefs)
2006                {
2007                   strcat(command, " -D");
2008                   strcat(command, item);
2009                }
2010
2011                // Execute it
2012                if((dep = DualPipeOpen(PipeOpenMode { output = true, error = true, input = false }, command)))
2013                {
2014                   char line[1024];
2015                   bool firstLine = true;
2016                   bool result = true;
2017
2018                   // To do some time: auto save external dependencies?
2019
2020                   while(!dep.Eof())
2021                   {
2022                      if(dep.GetLine(line, sizeof(line)-1))
2023                      {
2024                         if(firstLine)
2025                         {
2026                            char * colon = strstr(line, ":");
2027                            if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
2028                            {
2029                               result = false;
2030                               break;
2031                            }
2032                            firstLine = false;
2033                         }
2034                         f.Puts(line);
2035                         f.Puts("\n");
2036                      }
2037                      if(!result) break;
2038                   }
2039                   delete dep;
2040
2041                   // If we failed to generate dependencies...
2042                   if(!result)
2043                   {
2044
2045                   }
2046                }
2047             }
2048 #endif
2049          */
2050             if(!strcmpi(extension, "rc"))
2051             {
2052                ifCount++;
2053                f.Puts("ifdef WINDOWS_TARGET\n\n");
2054             }
2055             else
2056                OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
2057
2058             if(!strcmpi(extension, "ec"))
2059                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
2060             else
2061                f.Printf("$(OBJ)%s%s%s.o: %s%s.%s\n", moduleName,
2062                      collision ? "." : "", collision ? extension : "", modulePath, moduleName, extension);
2063             if(!strcmpi(extension, "cc") || !strcmpi(extension, "cpp") || !strcmpi(extension, "cxx"))
2064                f.Printf("\t$(CXX)");
2065             else if(!strcmpi(extension, "rc"))
2066                f.Printf("\t$(WINDRES) $(WINDRES_FLAGS) $< \"$(call escspace,$(call quote_path,$@))\"\n");
2067             else
2068                f.Printf("\t$(CC)");
2069
2070             if(strcmpi(extension, "rc") != 0)
2071             {
2072                f.Puts(" $(CFLAGS)");
2073                GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
2074
2075                if(!strcmpi(extension, "ec"))
2076                   f.Printf(" $(FVISIBILITY) -c $(call quote_path,$(OBJ)%s.c) -o $(call quote_path,$@)\n", moduleName);
2077                else
2078                   f.Printf(" -c $(call quote_path,%s%s.%s) -o $(call quote_path,$@)\n",
2079                         modulePath, moduleName, !strcmpi(extension, "ec") ? "c" : extension);
2080             }
2081             if(ifCount) f.Puts("endif\n");
2082             f.Puts("\n");
2083          }
2084       }
2085       if(files)
2086       {
2087          bool needed = false;
2088          for(child : files)
2089          {
2090             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2091             {
2092                needed = true;
2093                break;
2094             }
2095          }
2096          if(needed)
2097          {
2098             for(child : files)
2099             {
2100                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2101                   child.GenMakefilePrintObjectRules(f, project, namesInfo, prjConfig, /*excludedPlatforms,*/
2102                         nodeCFlagsMapping, nodeECFlagsMapping);
2103             }
2104          }
2105       }
2106       delete platforms;
2107    }
2108
2109    void GenMakefileAddResources(File f, String resourcesPath, ProjectConfig prjConfig)
2110    {
2111       int count = 0;
2112       if(files)
2113       {
2114          int c;
2115          bool prev = false;
2116          //Iterator<ProjectNode> i { files };
2117          //Iterator<ProjectNode> prev { files };
2118          //for(child : files)
2119          //while(i.Next())
2120          for(c = 0; c < files.count; c++)
2121          {
2122             ProjectNode child = files[c];
2123             TwoStrings ts = child.GetPlatformSpecificFu(prjConfig);
2124             if(count > 0 && ts)
2125                prev = true;
2126             if(child.type == file && !child.GetIsExcluded(prjConfig) && !(count > 0 && ts))
2127             {
2128                bool useRes;
2129                char tempPath[MAX_LOCATION];
2130                char resPath[MAX_LOCATION];
2131
2132                // $(EAR) aw%s --- /*quiet ? "q" : */""
2133                if(count == 0)
2134                   f.Printf("\t%s$(EAR) aw$(EARFLAGS) $(TARGET)", ts.a);
2135
2136                tempPath[0] = '\0';
2137                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
2138                {
2139                   useRes = true;
2140                   PathCatSlash(tempPath, child.name);
2141                }
2142                else
2143                {
2144                   useRes = false;
2145                   strcpy(tempPath, child.path);
2146                   PathCatSlash(tempPath, child.name);
2147                }
2148                EscapeForMake(resPath, tempPath, false, true, false);
2149                f.Printf(" %s%s", useRes ? "$(RES)" : "", resPath);
2150                count++;
2151             }
2152             if(count == 10 || (count > 0 && (ts || !child.next)))
2153             {
2154                char path[MAX_LOCATION] = "", temp[MAX_LOCATION];
2155                ProjectNode parent;
2156
2157                for(parent = this; parent.type == folder; parent = parent.parent)
2158                {
2159                   strcpy(temp, path);
2160                   strcpy(path, parent.name);
2161                   if(temp[0])
2162                   {
2163                      strcat(path, "/");
2164                      strcat(path, temp);
2165                   }
2166                }
2167                f.Printf(" \"%s\"%s\n", path, ts.b);
2168                count = 0;
2169                if(prev)
2170                {
2171                   c--;
2172                   prev = false;
2173                }
2174             }
2175             delete ts;
2176          }
2177          for(child : files)
2178          {
2179             if(child.type == folder)
2180                child.GenMakefileAddResources(f, resourcesPath, prjConfig);
2181          }
2182       }
2183    }
2184
2185    void GenMakeCollectAssignNodeFlags(ProjectConfig prjConfig, bool prjWithEcFiles,
2186          Map<String, int> cflagsVariations, Map<intptr, int> nodeCFlagsMapping,
2187          Map<String, int> ecflagsVariations, Map<intptr, int> nodeECFlagsMapping,
2188          Map<Platform, ProjectOptions> parentByPlatformOptions)
2189    {
2190       Map<Platform, ProjectOptions> byPlatformOptions = parentByPlatformOptions;
2191       if(type == file || type == folder || type == project)
2192       {
2193          bool hasPerNodeOptions = type == project;
2194          if(!hasPerNodeOptions)
2195          {
2196             if(options && !options.isEmpty)
2197                hasPerNodeOptions = true;
2198             else if(configurations)
2199             {
2200                for(c : configurations)
2201                {
2202                   if(c.options && !c.options.isEmpty)
2203                   {
2204                      hasPerNodeOptions = true;
2205                      break;
2206                   }
2207                   if(c.platforms)
2208                   {
2209                      for(p : c.platforms)
2210                      {
2211                         if(p.options && !p.options.isEmpty)
2212                         {
2213                            hasPerNodeOptions = true;
2214                            break;
2215                         }
2216                      }
2217                      if(hasPerNodeOptions)
2218                         break;
2219                   }
2220                }
2221             }
2222             if(!hasPerNodeOptions && platforms)
2223             {
2224                for(p : platforms)
2225                {
2226                   if(p.options && !p.options.isEmpty)
2227                   {
2228                      hasPerNodeOptions = true;
2229                      break;
2230                   }
2231                }
2232             }
2233
2234          }
2235          if(hasPerNodeOptions)
2236          {
2237             bool isEqual = false, isGreater = false;
2238             ComplexComparison complexCmp;
2239             DynamicString s;
2240             Map<Platform, ProjectOptions> additionsByPlatformOptions { };
2241             ProjectOptions platformsCommonOptions;
2242             ProjectOptions byFileConfigPlatformProjectOptions;
2243
2244             DynamicString cflags { };
2245             DynamicString ecflags { };
2246
2247             Platform platform;
2248
2249             byPlatformOptions = { };
2250
2251             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
2252             {
2253                byFileConfigPlatformProjectOptions =
2254                      BlendFileConfigPlatformProjectOptions(this, prjConfig, platform);
2255                byPlatformOptions[platform] = byFileConfigPlatformProjectOptions;
2256             }
2257
2258             CollectPlatformsCommonOptions(byPlatformOptions, &platformsCommonOptions);
2259
2260             byPlatformOptions[unknown] = platformsCommonOptions;
2261
2262             if(parentByPlatformOptions)
2263             {
2264                complexCmp = PlatformsOptionsGreaterEqual(byPlatformOptions,
2265                      parentByPlatformOptions, additionsByPlatformOptions);
2266                isGreater = complexCmp == greater;
2267                isEqual = complexCmp == equal;
2268             }
2269
2270             if(!isEqual)
2271             {
2272                for(platform = (Platform)1; platform < Platform::enumSize; platform++)
2273                {
2274                   byFileConfigPlatformProjectOptions = isGreater ? additionsByPlatformOptions[platform] : byPlatformOptions[platform];
2275                   s = { };
2276                   GenCFlagsFromProjectOptions(byFileConfigPlatformProjectOptions, prjWithEcFiles, false, isGreater, s);
2277                   if(s.count > 1)
2278                      cflags.concatf(" \\\n\t $(if $(%s),%s,)", PlatformToMakefileTargetVariable(platform), (String)s);
2279                   delete s;
2280                   s = { };
2281                   GenECFlagsFromProjectOptions(byFileConfigPlatformProjectOptions, prjWithEcFiles, s);
2282                   if(s.count > 1)
2283                      ecflags.concatf(" \\\n\t $(if $(%s),%s,)", PlatformToMakefileTargetVariable(platform), (String)s);
2284                   delete s;
2285                }
2286
2287                platformsCommonOptions = isGreater ? additionsByPlatformOptions[unknown] : byPlatformOptions[unknown];
2288                s = { };
2289                GenCFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, true, isGreater, s);
2290                if(s.count > 1)
2291                {
2292                   if(!isGreater) cflags.concat(" \\\n\t");
2293                   cflags.concat(s);
2294                }
2295                delete s;
2296                s = { };
2297                GenECFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, s);
2298                if(s.count > 1)
2299                {
2300                   ecflags.concat(" \\\n\t");
2301                   ecflags.concat(s);
2302                }
2303                delete s;
2304
2305                if(isGreater)
2306                {
2307                   cflags.concat(" \\\n\t");
2308                   DynStringPrintNodeFlagsVariable(parent, nodeCFlagsMapping, "PRJ_CFLAGS", cflags);
2309                }
2310             }
2311
2312             additionsByPlatformOptions.Free();
2313             delete additionsByPlatformOptions;
2314
2315             // output
2316             {
2317                if(isEqual)
2318                {
2319                   nodeCFlagsMapping[(intptr)this] = nodeCFlagsMapping[(intptr)parent];
2320                   nodeECFlagsMapping[(intptr)this] = nodeECFlagsMapping[(intptr)parent];
2321                }
2322                else
2323                {
2324                   String s;
2325                   int variationNum;
2326
2327                   variationNum = 1;
2328                   if((s = cflags) && s[0] && !(variationNum = cflagsVariations[s]))
2329                      cflagsVariations[s] = variationNum = cflagsVariations.count;
2330                   nodeCFlagsMapping[(intptr)this] = variationNum;
2331
2332                   variationNum = 1;
2333                   if((s = ecflags) && s[0] && !(variationNum = ecflagsVariations[s]))
2334                      ecflagsVariations[s] = variationNum = ecflagsVariations.count;
2335                   nodeECFlagsMapping[(intptr)this] = variationNum;
2336                }
2337             }
2338
2339             delete cflags;
2340             delete ecflags;
2341          }
2342          else
2343          {
2344             // output
2345             {
2346                nodeCFlagsMapping[(intptr)this] = nodeCFlagsMapping[(intptr)parent];
2347                nodeECFlagsMapping[(intptr)this] = nodeECFlagsMapping[(intptr)parent];
2348             }
2349          }
2350
2351       }
2352       if(files)
2353       {
2354          for(child : files)
2355          {
2356             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2357                child.GenMakeCollectAssignNodeFlags(prjConfig, prjWithEcFiles,
2358                      cflagsVariations, nodeCFlagsMapping, ecflagsVariations, nodeECFlagsMapping,
2359                      byPlatformOptions);
2360          }
2361       }
2362
2363       if(byPlatformOptions != parentByPlatformOptions)
2364       {
2365          byPlatformOptions.Free();
2366          delete byPlatformOptions;
2367       }
2368    }
2369
2370    Array<Platform> GetPlatformsArrayFromExclusionInfo(ProjectConfig prjConfig)
2371    {
2372       Array<Platform> platforms { };
2373       Map<Platform, SetBool> exclusionInfo { };
2374       CollectExclusionInfo(exclusionInfo, prjConfig);
2375
2376       if(exclusionInfo[unknown] == true)
2377       {
2378          if(exclusionInfo.count > 1)
2379             for(p : exclusionInfo; p == false)
2380                platforms.Add(&p);
2381       }
2382       else
2383       {
2384          bool onlyOnknown = true;
2385          for(p : exclusionInfo)
2386             if(&p != unknown && p == true)
2387             {
2388                onlyOnknown = false;
2389                break;
2390             }
2391          if(onlyOnknown)
2392             platforms.Add(unknown);
2393          else
2394          {
2395             Platform p;
2396             for(p = unknown + 1; p < Platform::enumSize; p++)
2397                if(exclusionInfo[p] != true)
2398                   platforms.Add(p);
2399          }
2400       }
2401       delete exclusionInfo;
2402       return platforms;
2403    }
2404
2405    void GetTargets(ProjectConfig prjConfig, Map<String, NameCollisionInfo> namesInfo, char * objDir, DynamicString output)
2406    {
2407       char moduleName[MAX_FILENAME];
2408       if(type == file)
2409       {
2410          bool headerAltFailed = false;
2411          bool collision;
2412          char extension[MAX_EXTENSION];
2413          NameCollisionInfo info;
2414          Project prj = property::project;
2415          Map<String, const String> headerToSource { [ { "eh", "ec" }, { "h", "c" }, { "hh", "cc" }, { "hpp", "cpp" }, { "hxx", "cxx" } ] };
2416
2417          GetExtension(name, extension);
2418          strcpy(moduleName, name);
2419          StripExtension(moduleName);
2420          info = namesInfo[moduleName];
2421          collision = info ? info.IsExtensionColliding(extension) : false;
2422
2423          for(h2s : headerToSource)
2424          {
2425             if(!strcmpi(extension, &h2s))
2426             {
2427                char filePath[MAX_LOCATION];
2428                GetFullFilePath(filePath);
2429                OutputLog($"No compilation required for header file "); OutputLog(filePath); OutputLog("\n");
2430                ChangeExtension(moduleName, h2s, moduleName);
2431                if(prj.topNode.Find(moduleName, false))
2432                {
2433                   strcpy(extension, h2s);
2434                   collision = info ? info.IsExtensionColliding(extension) : false;
2435                   ChangeExtension(filePath, h2s, filePath);
2436                   OutputLog($"Compiling source file "); OutputLog(filePath); OutputLog($" instead\n");
2437                   StripExtension(moduleName);
2438                }
2439                else
2440                {
2441                   headerAltFailed = true;
2442                   OutputLog($"Unable to locate source file "); OutputLog(moduleName); OutputLog($" to compile instead of "); OutputLog(filePath); OutputLog($"\n");
2443                   StripExtension(moduleName);
2444                }
2445                break;
2446             }
2447          }
2448
2449          if(!headerAltFailed)
2450          {
2451             output.concat(" \"");
2452             output.concat(objDir); //.concat(" $(OBJ)");
2453             output.concat("/");
2454
2455             if(collision)
2456             {
2457                strcat(moduleName, ".");
2458                strcat(moduleName, extension);
2459             }
2460             strcat(moduleName, ".o");
2461             output.concat(moduleName);
2462             output.concat("\"");
2463          }
2464       }
2465       else if(type == project && ContainsFilesWithExtension("ec", prjConfig))
2466       {
2467          Project prj = property::project;
2468
2469          ReplaceSpaces(moduleName, prj.moduleName);
2470          strcat(moduleName, ".main.ec");
2471          output.concat(" \"");
2472          output.concat(objDir);
2473          output.concat("/");
2474          output.concat(moduleName);
2475          output.concat("\"");
2476
2477          ChangeExtension(moduleName, "c", moduleName);
2478          output.concat(" \"");
2479          output.concat(objDir);
2480          output.concat("/");
2481          output.concat(moduleName);
2482          output.concat("\"");
2483
2484          ChangeExtension(moduleName, "o", moduleName);
2485          output.concat(" \"");
2486          output.concat(objDir);
2487          output.concat("/");
2488          output.concat(moduleName);
2489          output.concat("\"");
2490       }
2491       else if(files)
2492       {
2493          for(child : files)
2494          {
2495             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2496                child.GetTargets(prjConfig, namesInfo, objDir, output);
2497          }
2498       }
2499    }
2500
2501    void DeleteIntermediateFiles(CompilerConfig compiler, ProjectConfig prjConfig, int bitDepth, Map<String, NameCollisionInfo> namesInfo, bool onlyCObject)
2502    {
2503       if(type == file)
2504       {
2505          bool collision;
2506          char extension[MAX_EXTENSION];
2507          char fileName[MAX_FILENAME];
2508          char moduleName[MAX_FILENAME];
2509          NameCollisionInfo info;
2510          Project prj = property::project;
2511          DirExpression objDir = prj.GetObjDir(compiler, prjConfig, bitDepth);
2512
2513          GetExtension(name, extension);
2514          ReplaceSpaces(moduleName, name);
2515          StripExtension(moduleName);
2516          info = namesInfo[moduleName];
2517          collision = info ? info.IsExtensionColliding(extension) : false;
2518
2519          strcpy(fileName, prj.topNode.path);
2520          PathCatSlash(fileName, objDir.dir);
2521          PathCatSlash(fileName, name);
2522
2523          if(!onlyCObject && !strcmp(extension, "ec"))
2524          {
2525             ChangeExtension(fileName, "c", fileName);
2526             if(FileExists(fileName)) DeleteFile(fileName);
2527             ChangeExtension(fileName, "sym", fileName);
2528             if(FileExists(fileName)) DeleteFile(fileName);
2529             ChangeExtension(fileName, "imp", fileName);
2530             if(FileExists(fileName)) DeleteFile(fileName);
2531             ChangeExtension(fileName, "bowl", fileName);
2532             if(FileExists(fileName)) DeleteFile(fileName);
2533             ChangeExtension(fileName, "ec", fileName);
2534          }
2535
2536          if(collision)
2537             strcat(fileName, ".o");
2538          else
2539             ChangeExtension(fileName, "o", fileName);
2540          if(FileExists(fileName)) DeleteFile(fileName);
2541
2542          delete objDir;
2543       }
2544       else if(files)
2545       {
2546          for(child : files)
2547          {
2548             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2549                child.DeleteIntermediateFiles(compiler, prjConfig, bitDepth, namesInfo, onlyCObject);
2550          }
2551       }
2552    }
2553
2554    bool IsInNode(ProjectNode node)
2555    {
2556       bool result = false;
2557       ProjectNode n;
2558       for(n = this; n; n = n.parent)
2559       {
2560          if(n == node)
2561          {
2562             result = true;
2563             break;
2564          }
2565       }
2566       return result;
2567    }
2568 }
2569
2570 // the code in this function is closely matched to OptionsBox::Load
2571 // and accompanying derivations of OptionBox and their use of OptionSet,
2572 // OptionCheck, LoadOption and FinalizeLoading methods.
2573 // output changing modification should be mirrored in both implementations
2574 static ProjectOptions BlendFileConfigPlatformProjectOptions(ProjectNode node, ProjectConfig projectConfig, Platform platform)
2575 {
2576    ProjectOptions output { };
2577
2578    // legend: e Element
2579    //         o Option (of a ProjectOptions)
2580    //         n Node (ProjectNode)
2581    //         p Platform
2582    //         u Utility (GenericOptionTools)
2583
2584    int e;
2585    int o;
2586    int priority = 0;
2587    int includeDirsOption = OPTION(includeDirs);
2588    ProjectNode n;
2589    const char * platformName = platform ? platform.OnGetString(0,0,0) : null;
2590
2591    // OPTION(ProjectOptions' last member) for size
2592    Array<bool> optionConfigXplatformSet   { size = OPTION(installCommands) };
2593    Array<bool> optionDone                 { size = OPTION(installCommands) };
2594    Array<Array<String>> optionTempStrings { size = OPTION(installCommands) };
2595
2596    GenericOptionTools<SetBool>              utilSetBool {
2597       bool OptionCheck(ProjectOptions options, int option) {
2598          return *(SetBool*)((byte *)options + option) == true;
2599       }
2600       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2601          if(options && (*(SetBool*)((byte *)options + option) == true))
2602             *(SetBool*)((byte *)output + option) = true;
2603       }
2604    };
2605    GenericOptionTools<String>               utilString {
2606       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2607          String * string = (String*)((byte *)output + option);
2608          if(*string) delete *string;
2609          if(options)
2610             *string = CopyString(*(String*)((byte *)options + option));
2611       }
2612    };
2613    StringArrayOptionTools                   utilStringArrays {
2614       mergeValues = true;
2615       caseSensitive = true;
2616       bool OptionCheck(ProjectOptions options, int option) {
2617          Array<String> strings = *(Array<String>*)((byte *)options + option);
2618          return strings && strings.count;
2619       }
2620       bool OptionSet(ProjectOptions options, int option) {
2621          Array<String> strings = *(Array<String>*)((byte *)options + option);
2622          if(mergeValues && !configReplaces)
2623             return strings && strings.count;
2624          else
2625             return strings != null;
2626       }
2627       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2628          if(mergeValues)
2629          {
2630             Array<String> strings = options ? *(Array<String>*)((byte *)options + option) : null;
2631             if(strings)
2632             {
2633                int order = 0;
2634                Array<String> tempStrings = optionTempStrings[option];
2635                if(!tempStrings)
2636                   optionTempStrings[option] = tempStrings = { };
2637                for(s : strings)
2638                {
2639                   bool found = false;
2640                   char priorityMark[10];
2641                   order++;
2642                   if(priority)
2643                      sprintf(priorityMark, "%06d\n", priority * 1000 + order);
2644                   for(i : tempStrings; !(caseSensitive ? strcmp : strcmpi)(i, s)) { found = true; break; }
2645                   if(!found) tempStrings.Add(priority ? PrintString(priorityMark, s) : CopyString(s));
2646                }
2647             }
2648          }
2649          else
2650          {
2651             Array<String> * newStrings = (Array<String>*)((byte *)options + option);
2652             Array<String> * strings = (Array<String>*)((byte *)output + option);
2653             if(*strings) { strings->Free(); delete *strings; }
2654             if(*newStrings && newStrings->count) { *strings = { }; strings->Copy((void*)*newStrings); }
2655          }
2656       }
2657       void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2658          if(mergeValues)
2659          {
2660             Array<String> tempStrings = optionTempStrings[option];
2661             Array<String> * strings = (Array<String>*)((byte *)output + option);
2662             if(*strings) { strings->Free(); delete *strings; }
2663             if(tempStrings && tempStrings.count) { *strings = { }; strings->Copy((void*)tempStrings); }
2664             delete tempStrings;
2665          }
2666       }
2667    };
2668    GenericOptionTools<WarningsOption>       utilWarningsOption {
2669       bool OptionCheck(ProjectOptions options, int option) {
2670          WarningsOption value = *(WarningsOption*)((byte *)options + option);
2671          return value && value != none;
2672       }
2673       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2674          WarningsOption value = options ? *(WarningsOption*)((byte *)options + option) : (WarningsOption)0;
2675          *(WarningsOption*)((byte *)output + option) = value;
2676       }
2677    };
2678    GenericOptionTools<OptimizationStrategy> utilOptimizationStrategy {
2679       bool OptionCheck(ProjectOptions options, int option) {
2680          OptimizationStrategy value = *(OptimizationStrategy*)((byte *)options + option);
2681          return value && value != none;
2682       }
2683       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2684          OptimizationStrategy value = options ? *(OptimizationStrategy*)((byte *)options + option) : (OptimizationStrategy)0;
2685          *(OptimizationStrategy*)((byte *)output + option) = value;
2686       }
2687    };
2688
2689    Map<int, GenericOptionTools> ot { };
2690
2691    // The following are compiler options
2692
2693    ot[OPTION(debug)] =                   utilSetBool;
2694    ot[OPTION(memoryGuard)] =             utilSetBool;
2695    ot[OPTION(profile)] =                 utilSetBool;
2696    ot[OPTION(noLineNumbers)] =           utilSetBool;
2697    ot[OPTION(strictNameSpaces)] =        utilSetBool;
2698    ot[OPTION(fastMath)] =                utilSetBool;
2699
2700    ot[OPTION(defaultNameSpace)] =        utilString;
2701
2702    ot[OPTION(preprocessorDefinitions)] = utilStringArrays;
2703    ot[OPTION(includeDirs)] =             utilStringArrays;
2704
2705    ot[OPTION(warnings)] =                utilWarningsOption;
2706
2707    ot[OPTION(optimization)] =            utilOptimizationStrategy;
2708
2709    for(n = node; n; n = n.parent)
2710    {
2711       ProjectConfig nodeConfig = null;
2712       if(n.parent)
2713          priority = (priority / 10 + 1) * 10;
2714       else
2715          priority = 9990;
2716       if(projectConfig && n.configurations)
2717       {
2718          for(c : n.configurations; !strcmpi(c.name, projectConfig.name))
2719          {
2720             if(platform && c.platforms)
2721             {
2722                for(p : c.platforms; !strcmpi(p.name, platformName))
2723                {
2724                   for(uu : ot)
2725                   {
2726                      GenericOptionTools u = uu;
2727                      o = &uu;
2728                      if(!optionDone[o] && p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
2729                      {
2730                         u.LoadOption(p.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
2731                         if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2732                         optionConfigXplatformSet[o] = true;
2733                      }
2734                   }
2735                   break;
2736                }
2737             }
2738             nodeConfig = c;
2739             break;
2740          }
2741       }
2742       for(uu : ot)
2743       {
2744          GenericOptionTools u = uu;
2745          o = &uu;
2746          if(!optionDone[o])
2747          {
2748             if(platform && n.platforms && (!optionConfigXplatformSet[o] || !u.configReplaces))
2749             {
2750                for(p : n.platforms; !strcmpi(p.name, platformName))
2751                {
2752                   if(p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
2753                   {
2754                      u.LoadOption(p.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2755                      if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2756                   }
2757                   break;
2758                }
2759             }
2760             if(!optionDone[o] && nodeConfig && nodeConfig.options &&
2761                   ((u.mergeValues && !u.configReplaces) ?
2762                         u.OptionCheck(nodeConfig.options, o) :
2763                         u.OptionSet(nodeConfig.options, o)))
2764             {
2765                u.LoadOption(nodeConfig.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
2766                if(!u.mergeValues || u.configReplaces) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2767             }
2768             if(!optionDone[o])
2769             {
2770                if(n.options && (u.mergeValues ? u.OptionCheck(n.options, o) : u.OptionSet(n.options, o)))
2771                {
2772                   u.LoadOption(n.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2773                   if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2774                }
2775                else if(!n.parent)
2776                {
2777                   u.LoadOption(null, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2778                   if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2779                }
2780             }
2781          }
2782       }
2783    }
2784    for(uu : ot)
2785    {
2786       GenericOptionTools u = uu;
2787       o = &uu;
2788       if(!optionDone[o])
2789          u.FinalizeLoading(o, optionTempStrings, output);
2790    }
2791
2792    delete optionConfigXplatformSet;
2793    delete optionDone;
2794    delete optionTempStrings;
2795
2796    delete utilSetBool;
2797    delete utilString;
2798    delete utilStringArrays;
2799    delete utilWarningsOption;
2800    delete utilOptimizationStrategy;
2801
2802    delete ot;
2803
2804    return output;
2805 }
2806
2807 static void CollectPlatformsCommonOptions(Map<Platform, ProjectOptions> byPlatformOptions, ProjectOptions * platformsCommonOptions)
2808 {
2809    char * s;
2810    int i;
2811    ProjectOptions first;
2812    ProjectOptions commonOptions;
2813
2814    Map<String, int> countIncludeDirs { };
2815    Map<String, int> countPreprocessorDefinitions { };
2816    Map<String, bool> commonIncludeDirs { };
2817    Map<String, bool> commonPreprocessorDefinitions { };
2818
2819    for(options : byPlatformOptions) { first = options; break; }
2820
2821    *platformsCommonOptions = commonOptions = first.Copy();
2822
2823    if(commonOptions.includeDirs)
2824       commonOptions.includeDirs.Free();
2825    if(commonOptions.preprocessorDefinitions)
2826       commonOptions.preprocessorDefinitions.Free();
2827
2828    for(options : byPlatformOptions)
2829    {
2830       if(options != first)
2831       {
2832          if(commonOptions.debug && options.debug != commonOptions.debug)
2833             commonOptions.debug = unset;
2834          if(commonOptions.memoryGuard && options.memoryGuard != commonOptions.memoryGuard)
2835             commonOptions.memoryGuard = unset;
2836          if(commonOptions.profile && options.profile != commonOptions.profile)
2837             commonOptions.profile = unset;
2838          if(commonOptions.noLineNumbers && options.noLineNumbers != commonOptions.noLineNumbers)
2839             commonOptions.noLineNumbers = unset;
2840          if(commonOptions.strictNameSpaces && options.strictNameSpaces != commonOptions.strictNameSpaces)
2841             commonOptions.strictNameSpaces = unset;
2842          if(commonOptions.fastMath && options.fastMath != commonOptions.fastMath)
2843             commonOptions.fastMath = unset;
2844
2845          if(commonOptions.warnings && options.warnings != commonOptions.warnings)
2846             commonOptions.warnings = unset;
2847          if(commonOptions.optimization && options.optimization != commonOptions.optimization)
2848             commonOptions.optimization = unset;
2849
2850          if(commonOptions.defaultNameSpace && strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
2851             delete commonOptions.defaultNameSpace;
2852       }
2853
2854       CountSameNonEmptyOrNullStrings(options.includeDirs, countIncludeDirs);
2855       CountSameNonEmptyOrNullStrings(options.preprocessorDefinitions, countPreprocessorDefinitions);
2856    }
2857
2858    GetPlatformsCommonStrings(countIncludeDirs, byPlatformOptions.count,
2859          commonIncludeDirs, commonOptions.includeDirs);
2860    GetPlatformsCommonStrings(countPreprocessorDefinitions, byPlatformOptions.count,
2861          commonPreprocessorDefinitions, commonOptions.preprocessorDefinitions);
2862
2863    for(options : byPlatformOptions)
2864    {
2865       if(options.debug && options.debug == commonOptions.debug)
2866          options.debug = unset;
2867       if(options.memoryGuard && options.memoryGuard == commonOptions.memoryGuard)
2868          options.memoryGuard = unset;
2869       if(options.profile && options.profile == commonOptions.profile)
2870          options.profile = unset;
2871       if(options.noLineNumbers && options.noLineNumbers == commonOptions.noLineNumbers)
2872          options.noLineNumbers = unset;
2873       if(options.strictNameSpaces && options.strictNameSpaces == commonOptions.strictNameSpaces)
2874          options.strictNameSpaces = unset;
2875       if(options.fastMath && options.fastMath == commonOptions.fastMath)
2876          options.fastMath = unset;
2877
2878       if(options.warnings && options.warnings == commonOptions.warnings)
2879          options.warnings = unset;
2880       if(options.optimization && options.optimization == commonOptions.optimization)
2881          options.optimization = unset;
2882
2883       if(options.defaultNameSpace && !strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
2884          delete options.defaultNameSpace;
2885
2886       RemovePlatformsCommonStrings(commonIncludeDirs, options.includeDirs);
2887       RemovePlatformsCommonStrings(commonPreprocessorDefinitions, options.preprocessorDefinitions);
2888    }
2889
2890    delete countIncludeDirs;
2891    delete countPreprocessorDefinitions;
2892    delete commonIncludeDirs;
2893    delete commonPreprocessorDefinitions;
2894 }
2895
2896 static ComplexComparison PlatformsOptionsGreaterEqual(Map<Platform, ProjectOptions> byPlatformOptions,
2897       Map<Platform, ProjectOptions> parentByPlatformOptions,
2898       Map<Platform, ProjectOptions> additionsByPlatformOptions)
2899 {
2900    ComplexComparison result = equal;
2901    ComplexComparison compare;
2902    Platform platform;
2903    for(platform = (Platform)0; platform < Platform::enumSize; platform++)
2904    {
2905       ProjectOptions additionalOptions;
2906       additionsByPlatformOptions[platform] = { };
2907       additionalOptions = additionsByPlatformOptions[platform];
2908       compare = ExtractPlatformsOptionsAdditions(byPlatformOptions[platform], parentByPlatformOptions[platform], additionalOptions);
2909       if(compare == greater && result == equal)
2910          result = greater;
2911       else if(compare == different)
2912       {
2913          result = different;
2914          break;
2915       }
2916    }
2917    return result;
2918 }
2919
2920 static ComplexComparison ExtractPlatformsOptionsAdditions(ProjectOptions options, ProjectOptions parentOptions, ProjectOptions additionalOptions)
2921 {
2922    ComplexComparison result = equal;
2923    if(options.debug != parentOptions.debug ||
2924          options.memoryGuard != parentOptions.memoryGuard ||
2925          options.profile != parentOptions.profile ||
2926          options.noLineNumbers != parentOptions.noLineNumbers ||
2927          options.strictNameSpaces != parentOptions.strictNameSpaces ||
2928          options.fastMath != parentOptions.fastMath ||
2929          options.warnings != parentOptions.warnings ||
2930          options.optimization != parentOptions.optimization ||
2931          (options.defaultNameSpace != parentOptions.defaultNameSpace &&
2932                strcmp(options.defaultNameSpace, parentOptions.defaultNameSpace)))
2933       result = different;
2934    else
2935    {
2936       if(!StringsAreSameOrMore(options.includeDirs, parentOptions.includeDirs, &additionalOptions.includeDirs) ||
2937             !StringsAreSameOrMore(options.preprocessorDefinitions, parentOptions.preprocessorDefinitions, &additionalOptions.preprocessorDefinitions))
2938          result = different;
2939       if((additionalOptions.includeDirs && additionalOptions.includeDirs.count) ||
2940             (additionalOptions.preprocessorDefinitions && additionalOptions.preprocessorDefinitions.count))
2941          result = greater;
2942    }
2943    return result;
2944 }
2945
2946 enum ComplexComparison { different/*, smaller*/, equal, greater };
2947
2948 static bool StringsAreSameOrMore(Array<String> strings, Array<String> originals, Array<String> * additions)
2949 {
2950    bool result = true;
2951    if((!strings || !strings.count) && originals && originals.count)
2952       result = false;
2953    else if(strings && strings.count && (!originals || !originals.count))
2954    {
2955       if(!*additions)
2956          *additions = { };
2957       for(s : strings)
2958          additions->Add(CopyString(s));
2959    }
2960    else if(strings && strings.count && originals && originals.count)
2961    {
2962       Map<String, String> map { };
2963       MapIterator<String, bool> mit { map = map };
2964       for(it : strings)
2965       {
2966          char * s = strstr(it, "\n");
2967          s = s ? s+1 : it;
2968          map[s] = it;
2969       }
2970       for(it : originals)
2971       {
2972          char * s = strstr(it, "\n");
2973          s = s ? s+1 : it;
2974          if(!mit.Index(s, false))
2975          {
2976             result = false;
2977             break;
2978          }
2979          else
2980             map[s] = null;
2981       }
2982       if(result)
2983       {
2984          if(!*additions)
2985             *additions = { };
2986          for(it : map)
2987          {
2988             if(it)
2989                additions->Add(CopyString(it));
2990          }
2991       }
2992       delete map;
2993    }
2994    return result;
2995 }
2996
2997 static void CountSameNonEmptyOrNullStrings(Array<String> strings, Map<String, int> counts)
2998 {
2999    if(strings)
3000    {
3001       for(it : strings)
3002       {
3003          char * s = it;
3004          if(s && s[0])
3005             counts[s]++;
3006       }
3007    }
3008 }
3009
3010 static void GetPlatformsCommonStrings(Map<String, int> counts, int goodCount, Map<String, bool> common, Array<String> strings)
3011 {
3012    for(it : counts)
3013    {
3014       int i = it;
3015       if(i == goodCount)
3016       {
3017          const char * s = &it;
3018          strings.Add(CopyString(s));
3019          common[s] = true;
3020       }
3021    }
3022 }
3023
3024 static void RemovePlatformsCommonStrings(Map<String, bool> common, Array<String> strings)
3025 {
3026    if(strings)
3027    {
3028       Array<String> tmp { };
3029       MapIterator<String, bool> mit { map = common };
3030       for(it : strings)
3031       {
3032          char * s = it;
3033          if(!mit.Index(s, false))
3034             tmp.Add(CopyString(s));
3035       }
3036       strings.Free();
3037       if(tmp.count)
3038       {
3039          for(s : tmp)
3040             strings.Add(CopyString(s));
3041          tmp.Free();
3042       }
3043       delete tmp;
3044    }
3045 }
3046
3047 static void GenMakePrintNodeFlagsVariable(ProjectNode node, Map<intptr, int> nodeFlagsMapping, const String variableName, File f)
3048 {
3049    int customFlags;
3050    customFlags = nodeFlagsMapping[(intptr)node];
3051    if(customFlags > 1)
3052       f.Printf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
3053    else
3054       f.Printf(" $(%s)", variableName);
3055 }
3056
3057 static void DynStringPrintNodeFlagsVariable(ProjectNode node, Map<intptr, int> nodeFlagsMapping, const String variableName, DynamicString s)
3058 {
3059    int customFlags;
3060    customFlags = nodeFlagsMapping[(intptr)node];
3061    if(customFlags > 1)
3062       s.concatf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
3063    else
3064       s.concatf(" $(%s)", variableName);
3065 }
3066
3067 static void GenCFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, bool commonOptions, bool isGreater, DynamicString s)
3068 {
3069    if(!isGreater)
3070    {
3071       //if(gccCompiler)
3072       {
3073          if(options.optimization == speed || options.optimization == size ||
3074                options.fastMath == true || options.debug == true)
3075          {
3076             if(options.debug != true)
3077             {
3078                s.concat(" $(if $(DEBUG),");
3079                s.concat(" -g");
3080                s.concat(",");
3081             }
3082             switch(options.optimization)
3083             {
3084                case speed: s.concat(" -O2"); break;
3085                case size: s.concat(" -Os"); break;
3086             }
3087             if(options.fastMath == true)
3088                s.concat(" -ffast-math");
3089             if(options.debug == true)
3090                s.concat(" -g");
3091             if(options.debug != true)
3092                s.concat(")");
3093          }
3094          else if(commonOptions)
3095             s.concat(" $(if $(DEBUG),-g)");
3096          if(commonOptions)
3097             s.concat(" $(FPIC)");
3098       }
3099       switch(options.warnings)
3100       {
3101          case all: s.concat(" -Wall"); break;
3102          case none: s.concat(" -w"); break;
3103       }
3104       if(options.profile)
3105          s.concat(" -pg");
3106       if(commonOptions)
3107          s.concat(" -DREPOSITORY_VERSION=\"\\\"$(REPOSITORY_VER)\\\"\"");
3108    }
3109
3110    if(options && options.preprocessorDefinitions)
3111       ListOptionToDynamicString(s, _D, options.preprocessorDefinitions, false, lineEach, "\t\t\t");
3112    if(options && options.includeDirs)
3113       ListOptionToDynamicString(s, _I, options.includeDirs, true, lineEach, "\t\t\t");
3114 }
3115
3116 static void GenECFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, DynamicString s)
3117 {
3118    if(options.memoryGuard == true)
3119       s.concat(" -memguard");
3120    if(options.noLineNumbers == true)
3121       s.concat(" -nolinenumbers");
3122    if(options.strictNameSpaces == true)
3123       s.concat(" -strictns");
3124    if(options.defaultNameSpace && options.defaultNameSpace[0])
3125       s.concatf(" -defaultns %s", options.defaultNameSpace);
3126 }
3127
3128 static void ListOptionToDynamicString(DynamicString output, ToolchainFlag flag, Array<String> list, bool prioritize,
3129       LineOutputMethod lineMethod, const String newLineStart)
3130 {
3131    if(list.count)
3132    {
3133       if(lineMethod == newLine)
3134       {
3135          output.concat(" \\\n");
3136          output.concat(newLineStart);
3137       }
3138       if(prioritize)
3139       {
3140          Map<String, int> sortedList { };
3141          MapNode<String, int> mn;
3142          for(item : list)
3143             sortedList[item] = 1;
3144          for(mn = sortedList.root.minimum; mn; mn = mn.next)
3145          {
3146             char * start = strstr(mn.key, "\n");
3147             if(lineMethod == lineEach)
3148             {
3149                output.concat(" \\\n");
3150                output.concat(newLineStart);
3151             }
3152             output.concat(" ");
3153             output.concat(flagNames[flag]);
3154             EscapeForMakeToDynString(output, start ? start+1 : mn.key, false, true, flag == _D);
3155          }
3156          delete sortedList;
3157       }
3158       else
3159       {
3160          for(item : list)
3161          {
3162             if(lineMethod == lineEach)
3163             {
3164                output.concat(" \\\n");
3165                output.concat(newLineStart);
3166             }
3167             output.concat(" ");
3168             output.concat(flagNames[flag]);
3169             EscapeForMakeToDynString(output, item, false, true, flag == _D);
3170          }
3171       }
3172    }
3173 }
3174
3175 class GenericOptionTools<class X>
3176 {
3177    bool mergeValues, configReplaces;
3178
3179    virtual bool OptionSet(ProjectOptions options, int option) {
3180       if(*(X*)((byte *)options + option))
3181          return true;
3182       return false;
3183    }
3184
3185    // BUG: OptionCheck = OptionSet; // Overrides derived classes OptionCheck ?
3186
3187    virtual bool OptionCheck(ProjectOptions options, int option) {
3188       return OptionSet(options, option);
3189    }
3190
3191    virtual void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output);
3192    virtual void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output);
3193 }
3194
3195 class StringArrayOptionTools : GenericOptionTools<Array<String>>
3196 {
3197    bool caseSensitive;
3198 }
3199
3200 class NameCollisionInfo
3201 {
3202    bool ec;
3203    bool s;
3204    bool c;
3205    bool rc;
3206    bool cpp;
3207    bool cc;
3208    bool cxx;
3209    bool m;
3210    bool mm;
3211    byte count;
3212
3213    bool IsExtensionColliding(char * extension)
3214    {
3215       bool colliding;
3216       if(count > 1 &&
3217             ((!strcmpi(extension, "c")   && ec) ||
3218              (!strcmpi(extension, "rc")  && (ec || c)) ||
3219              (!strcmpi(extension, "s")   && (ec || c || rc)) ||
3220              (!strcmpi(extension, "cpp") && (ec || c || rc || s)) ||
3221              (!strcmpi(extension, "cc")  && (ec || c || rc || s || cpp)) ||
3222              (!strcmpi(extension, "cxx") && (ec || c || rc || s || cpp || cc)) ||
3223              (!strcmpi(extension, "m")   && (ec || c || rc || s || cpp || cc || m)) ||
3224               !strcmpi(extension, "mm")))
3225          colliding = true;
3226       else
3227          colliding = false;
3228      return colliding;
3229    }
3230 }