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