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