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