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