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