ide; (#975) fixed space char in source code file name regression.
[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 || type == folder || !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             EscapeForMake(modulePath, tempPath, false, true, false);
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                EscapeForMake(modulePath, path, false, true, false);
1467                EscapeForMake(moduleName, name, false, true, false);
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                EscapeForMake(modulePath, path, true, true, false);
1478                EscapeForMake(moduleName, name, true, true, false);
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                EscapeForMake(modulePath, path, false, true, false);
1490                EscapeForMake(moduleName, name, false, true, false);
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                EscapeForMake(moduleName, name, false, true, false);
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 $(call escspace,$@)\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 $(call escspace,$@) -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) $< $(call escspace,$@)\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 $(call escspace,$@)\n", moduleName);
2059                else
2060                   f.Printf(" -c %s%s.%s -o $(call escspace,$@)\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                // $(EAR) aw%s --- /*quiet ? "q" : */""
2115                if(count == 0)
2116                   f.Printf("\t%s$(EAR) aw$(EARFLAGS) $(TARGET)", ts.a);
2117
2118                tempPath[0] = '\0';
2119                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
2120                {
2121                   useRes = true;
2122                   PathCatSlash(tempPath, child.name);
2123                }
2124                else
2125                {
2126                   useRes = false;
2127                   strcpy(tempPath, child.path);
2128                   PathCatSlash(tempPath, child.name);
2129                }
2130                EscapeForMake(resPath, tempPath, false, true, false);
2131                f.Printf(" %s%s", useRes ? "$(RES)" : "", resPath);
2132                count++;
2133             }
2134             if(count == 10 || (count > 0 && (ts || !child.next)))
2135             {
2136                char path[MAX_LOCATION] = "", temp[MAX_LOCATION];
2137                ProjectNode parent;
2138
2139                for(parent = this; parent.type == folder; parent = parent.parent)
2140                {
2141                   strcpy(temp, path);
2142                   strcpy(path, parent.name);
2143                   if(temp[0])
2144                   {
2145                      strcat(path, "/");
2146                      strcat(path, temp);
2147                   }
2148                }
2149                f.Printf(" \"%s\"%s\n", path, ts.b);
2150                count = 0;
2151                if(prev)
2152                {
2153                   c--;
2154                   prev = false;
2155                }
2156             }
2157             delete ts;
2158          }
2159          for(child : files)
2160          {
2161             if(child.type == folder)
2162                child.GenMakefileAddResources(f, resourcesPath, prjConfig);
2163          }
2164       }
2165    }
2166
2167    void GenMakeCollectAssignNodeFlags(ProjectConfig prjConfig, bool prjWithEcFiles,
2168          Map<String, int> cflagsVariations, Map<intptr, int> nodeCFlagsMapping,
2169          Map<String, int> ecflagsVariations, Map<intptr, int> nodeECFlagsMapping,
2170          Map<Platform, ProjectOptions> parentByPlatformOptions)
2171    {
2172       Map<Platform, ProjectOptions> byPlatformOptions = parentByPlatformOptions;
2173       if(type == file || type == folder || type == project)
2174       {
2175          bool hasPerNodeOptions = type == project;
2176          if(!hasPerNodeOptions)
2177          {
2178             if(options && !options.isEmpty)
2179                hasPerNodeOptions = true;
2180             else if(configurations)
2181             {
2182                for(c : configurations)
2183                {
2184                   if(c.options && !c.options.isEmpty)
2185                   {
2186                      hasPerNodeOptions = true;
2187                      break;
2188                   }
2189                   if(c.platforms)
2190                   {
2191                      for(p : c.platforms)
2192                      {
2193                         if(p.options && !p.options.isEmpty)
2194                         {
2195                            hasPerNodeOptions = true;
2196                            break;
2197                         }
2198                      }
2199                      if(hasPerNodeOptions)
2200                         break;
2201                   }
2202                }
2203             }
2204             if(!hasPerNodeOptions && platforms)
2205             {
2206                for(p : platforms)
2207                {
2208                   if(p.options && !p.options.isEmpty)
2209                   {
2210                      hasPerNodeOptions = true;
2211                      break;
2212                   }
2213                }
2214             }
2215
2216          }
2217          if(hasPerNodeOptions)
2218          {
2219             bool isEqual = false, isGreater = false;
2220             ComplexComparison complexCmp;
2221             DynamicString s;
2222             Map<Platform, ProjectOptions> additionsByPlatformOptions { };
2223             ProjectOptions platformsCommonOptions;
2224             ProjectOptions byFileConfigPlatformProjectOptions;
2225
2226             DynamicString cflags { };
2227             DynamicString ecflags { };
2228
2229             Platform platform;
2230
2231             byPlatformOptions = { };
2232
2233             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
2234             {
2235                byFileConfigPlatformProjectOptions =
2236                      BlendFileConfigPlatformProjectOptions(this, prjConfig, platform);
2237                byPlatformOptions[platform] = byFileConfigPlatformProjectOptions;
2238             }
2239
2240             CollectPlatformsCommonOptions(byPlatformOptions, &platformsCommonOptions);
2241
2242             byPlatformOptions[unknown] = platformsCommonOptions;
2243
2244             if(parentByPlatformOptions)
2245             {
2246                complexCmp = PlatformsOptionsGreaterEqual(byPlatformOptions,
2247                      parentByPlatformOptions, additionsByPlatformOptions);
2248                isGreater = complexCmp == greater;
2249                isEqual = complexCmp == equal;
2250             }
2251
2252             if(!isEqual)
2253             {
2254                for(platform = (Platform)1; platform < Platform::enumSize; platform++)
2255                {
2256                   byFileConfigPlatformProjectOptions = isGreater ? additionsByPlatformOptions[platform] : byPlatformOptions[platform];
2257                   s = { };
2258                   GenCFlagsFromProjectOptions(byFileConfigPlatformProjectOptions, prjWithEcFiles, false, isGreater, s);
2259                   if(s.count > 1)
2260                      cflags.concatf(" \\\n\t $(if $(%s),%s,)", PlatformToMakefileTargetVariable(platform), (String)s);
2261                   delete s;
2262                   s = { };
2263                   GenECFlagsFromProjectOptions(byFileConfigPlatformProjectOptions, prjWithEcFiles, s);
2264                   if(s.count > 1)
2265                      ecflags.concatf(" \\\n\t $(if $(%s),%s,)", PlatformToMakefileTargetVariable(platform), (String)s);
2266                   delete s;
2267                }
2268
2269                platformsCommonOptions = isGreater ? additionsByPlatformOptions[unknown] : byPlatformOptions[unknown];
2270                s = { };
2271                GenCFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, true, isGreater, s);
2272                if(s.count > 1)
2273                {
2274                   if(!isGreater) cflags.concat(" \\\n\t");
2275                   cflags.concat(s);
2276                }
2277                delete s;
2278                s = { };
2279                GenECFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, s);
2280                if(s.count > 1)
2281                {
2282                   ecflags.concat(" \\\n\t");
2283                   ecflags.concat(s);
2284                }
2285                delete s;
2286
2287                if(isGreater)
2288                {
2289                   cflags.concat(" \\\n\t");
2290                   DynStringPrintNodeFlagsVariable(parent, nodeCFlagsMapping, "PRJ_CFLAGS", cflags);
2291                }
2292             }
2293
2294             additionsByPlatformOptions.Free();
2295             delete additionsByPlatformOptions;
2296
2297             // output
2298             {
2299                if(isEqual)
2300                {
2301                   nodeCFlagsMapping[(intptr)this] = nodeCFlagsMapping[(intptr)parent];
2302                   nodeECFlagsMapping[(intptr)this] = nodeECFlagsMapping[(intptr)parent];
2303                }
2304                else
2305                {
2306                   String s;
2307                   int variationNum;
2308
2309                   variationNum = 1;
2310                   if((s = cflags) && s[0] && !(variationNum = cflagsVariations[s]))
2311                      cflagsVariations[s] = variationNum = cflagsVariations.count;
2312                   nodeCFlagsMapping[(intptr)this] = variationNum;
2313
2314                   variationNum = 1;
2315                   if((s = ecflags) && s[0] && !(variationNum = ecflagsVariations[s]))
2316                      ecflagsVariations[s] = variationNum = ecflagsVariations.count;
2317                   nodeECFlagsMapping[(intptr)this] = variationNum;
2318                }
2319             }
2320
2321             delete cflags;
2322             delete ecflags;
2323          }
2324          else
2325          {
2326             // output
2327             {
2328                nodeCFlagsMapping[(intptr)this] = nodeCFlagsMapping[(intptr)parent];
2329                nodeECFlagsMapping[(intptr)this] = nodeECFlagsMapping[(intptr)parent];
2330             }
2331          }
2332
2333       }
2334       if(files)
2335       {
2336          for(child : files)
2337          {
2338             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2339                child.GenMakeCollectAssignNodeFlags(prjConfig, prjWithEcFiles,
2340                      cflagsVariations, nodeCFlagsMapping, ecflagsVariations, nodeECFlagsMapping,
2341                      byPlatformOptions);
2342          }
2343       }
2344
2345       if(byPlatformOptions != parentByPlatformOptions)
2346       {
2347          byPlatformOptions.Free();
2348          delete byPlatformOptions;
2349       }
2350    }
2351
2352    Array<Platform> GetPlatformsArrayFromExclusionInfo(ProjectConfig prjConfig)
2353    {
2354       Array<Platform> platforms { };
2355       Map<Platform, SetBool> exclusionInfo { };
2356       CollectExclusionInfo(exclusionInfo, prjConfig);
2357
2358       if(exclusionInfo[unknown] == true)
2359       {
2360          if(exclusionInfo.count > 1)
2361             for(p : exclusionInfo; p == false)
2362                platforms.Add(&p);
2363       }
2364       else
2365       {
2366          bool onlyOnknown = true;
2367          for(p : exclusionInfo)
2368             if(&p != unknown && p == true)
2369             {
2370                onlyOnknown = false;
2371                break;
2372             }
2373          if(onlyOnknown)
2374             platforms.Add(unknown);
2375          else
2376          {
2377             Platform p;
2378             for(p = unknown + 1; p < Platform::enumSize; p++)
2379                if(exclusionInfo[p] != true)
2380                   platforms.Add(p);
2381          }
2382       }
2383       delete exclusionInfo;
2384       return platforms;
2385    }
2386
2387    void GetTargets(ProjectConfig prjConfig, Map<String, NameCollisionInfo> namesInfo, char * objDir, DynamicString output)
2388    {
2389       char moduleName[MAX_FILENAME];
2390       if(type == file)
2391       {
2392          bool headerAltFailed = false;
2393          bool collision;
2394          char extension[MAX_EXTENSION];
2395          NameCollisionInfo info;
2396          Project prj = property::project;
2397          Map<String, String> headerToSource { [ { "eh", "ec" }, { "h", "c" }, { "hh", "cc" }, { "hpp", "cpp" }, { "hxx", "cxx" } ] };
2398
2399          GetExtension(name, extension);
2400          ReplaceSpaces(moduleName, name);
2401          StripExtension(moduleName);
2402          info = namesInfo[moduleName];
2403          collision = info ? info.IsExtensionColliding(extension) : false;
2404
2405          for(h2s : headerToSource)
2406          {
2407             if(!strcmpi(extension, &h2s))
2408             {
2409                char filePath[MAX_LOCATION];
2410                GetFullFilePath(filePath);
2411                OutputLog($"No compilation required for header file "); OutputLog(filePath); OutputLog("\n");
2412                ChangeExtension(moduleName, h2s, moduleName);
2413                if(prj.topNode.Find(moduleName, false))
2414                {
2415                   strcpy(extension, h2s);
2416                   collision = info ? info.IsExtensionColliding(extension) : false;
2417                   ChangeExtension(filePath, h2s, filePath);
2418                   OutputLog($"Compiling source file "); OutputLog(filePath); OutputLog($" instead\n");
2419                   StripExtension(moduleName);
2420                }
2421                else
2422                {
2423                   headerAltFailed = true;
2424                   OutputLog($"Unable to locate source file "); OutputLog(moduleName); OutputLog($" to compile instead of "); OutputLog(filePath); OutputLog($"\n");
2425                   StripExtension(moduleName);
2426                }
2427                break;
2428             }
2429          }
2430
2431          if(!headerAltFailed)
2432          {
2433             output.concat(" \"");
2434             output.concat(objDir); //.concat(" $(OBJ)");
2435             output.concat("/");
2436
2437             if(collision)
2438             {
2439                strcat(moduleName, ".");
2440                strcat(moduleName, extension);
2441             }
2442             strcat(moduleName, ".o");
2443             output.concat(moduleName);
2444             output.concat("\"");
2445          }
2446       }
2447       else if(type == project && ContainsFilesWithExtension("ec"))
2448       {
2449          Project prj = property::project;
2450
2451          ReplaceSpaces(moduleName, prj.moduleName);
2452          strcat(moduleName, ".main.ec");
2453          output.concat(" \"");
2454          output.concat(objDir);
2455          output.concat("/");
2456          output.concat(moduleName);
2457          output.concat("\"");
2458
2459          ChangeExtension(moduleName, "c", moduleName);
2460          output.concat(" \"");
2461          output.concat(objDir);
2462          output.concat("/");
2463          output.concat(moduleName);
2464          output.concat("\"");
2465
2466          ChangeExtension(moduleName, "o", moduleName);
2467          output.concat(" \"");
2468          output.concat(objDir);
2469          output.concat("/");
2470          output.concat(moduleName);
2471          output.concat("\"");
2472       }
2473       else if(files)
2474       {
2475          for(child : files)
2476          {
2477             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2478                child.GetTargets(prjConfig, namesInfo, objDir, output);
2479          }
2480       }
2481    }
2482
2483    void DeleteIntermediateFiles(CompilerConfig compiler, ProjectConfig prjConfig, int bitDepth, Map<String, NameCollisionInfo> namesInfo, bool onlyCObject)
2484    {
2485       if(type == file)
2486       {
2487          bool collision;
2488          char extension[MAX_EXTENSION];
2489          char fileName[MAX_FILENAME];
2490          char moduleName[MAX_FILENAME];
2491          NameCollisionInfo info;
2492          Project prj = property::project;
2493          DirExpression objDir = prj.GetObjDir(compiler, prjConfig, bitDepth);
2494
2495          GetExtension(name, extension);
2496          ReplaceSpaces(moduleName, name);
2497          StripExtension(moduleName);
2498          info = namesInfo[moduleName];
2499          collision = info ? info.IsExtensionColliding(extension) : false;
2500
2501          strcpy(fileName, prj.topNode.path);
2502          PathCatSlash(fileName, objDir.dir);
2503          PathCatSlash(fileName, name);
2504
2505          if(!onlyCObject && !strcmp(extension, "ec"))
2506          {
2507             ChangeExtension(fileName, "c", fileName);
2508             if(FileExists(fileName)) DeleteFile(fileName);
2509             ChangeExtension(fileName, "sym", fileName);
2510             if(FileExists(fileName)) DeleteFile(fileName);
2511             ChangeExtension(fileName, "imp", fileName);
2512             if(FileExists(fileName)) DeleteFile(fileName);
2513             ChangeExtension(fileName, "bowl", fileName);
2514             if(FileExists(fileName)) DeleteFile(fileName);
2515             ChangeExtension(fileName, "ec", fileName);
2516          }
2517
2518          if(collision)
2519             strcat(fileName, ".o");
2520          else
2521             ChangeExtension(fileName, "o", fileName);
2522          if(FileExists(fileName)) DeleteFile(fileName);
2523
2524          delete objDir;
2525       }
2526       else if(files)
2527       {
2528          for(child : files)
2529          {
2530             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2531                child.DeleteIntermediateFiles(compiler, prjConfig, bitDepth, namesInfo, onlyCObject);
2532          }
2533       }
2534    }
2535
2536    bool IsInNode(ProjectNode node)
2537    {
2538       bool result = false;
2539       ProjectNode n;
2540       for(n = this; n; n = n.parent)
2541       {
2542          if(n == node)
2543          {
2544             result = true;
2545             break;
2546          }
2547       }
2548       return result;
2549    }
2550 }
2551
2552 // the code in this function is closely matched to OptionsBox::Load
2553 // and accompanying derivations of OptionBox and their use of OptionSet,
2554 // OptionCheck, LoadOption and FinalizeLoading methods.
2555 // output changing modification should be mirrored in both implementations
2556 static ProjectOptions BlendFileConfigPlatformProjectOptions(ProjectNode node, ProjectConfig projectConfig, Platform platform)
2557 {
2558    ProjectOptions output { };
2559
2560    // legend: e Element
2561    //         o Option (of a ProjectOptions)
2562    //         n Node (ProjectNode)
2563    //         p Platform
2564    //         u Utility (GenericOptionTools)
2565
2566    int e;
2567    int o;
2568    int priority = 0;
2569    int includeDirsOption = OPTION(includeDirs);
2570    ProjectNode n;
2571    char * platformName = platform ? platform.OnGetString(0,0,0) : null;
2572
2573    Array<bool> optionConfigXplatformSet   { size = OPTION(postbuildCommands) };
2574    Array<bool> optionDone                 { size = OPTION(postbuildCommands) };
2575    Array<Array<String>> optionTempStrings { size = OPTION(postbuildCommands) };
2576
2577    GenericOptionTools<SetBool>              utilSetBool {
2578       bool OptionCheck(ProjectOptions options, int option) {
2579          return *(SetBool*)((byte *)options + option) == true;
2580       }
2581       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2582          if(options && (*(SetBool*)((byte *)options + option) == true))
2583             *(SetBool*)((byte *)output + option) = true;
2584       }
2585    };
2586    GenericOptionTools<String>               utilString {
2587       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2588          String * string = (String*)((byte *)output + option);
2589          if(*string) delete *string;
2590          if(options)
2591             *string = CopyString(*(String*)((byte *)options + option));
2592       }
2593    };
2594    StringArrayOptionTools                   utilStringArrays {
2595       mergeValues = true;
2596       caseSensitive = true;
2597       bool OptionCheck(ProjectOptions options, int option) {
2598          Array<String> strings = *(Array<String>*)((byte *)options + option);
2599          return strings && strings.count;
2600       }
2601       bool OptionSet(ProjectOptions options, int option) {
2602          Array<String> strings = *(Array<String>*)((byte *)options + option);
2603          if(mergeValues && !configReplaces)
2604             return strings && strings.count;
2605          else
2606             return strings != null;
2607       }
2608       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2609          if(mergeValues)
2610          {
2611             Array<String> strings = options ? *((Array<String>*)((byte *)options + option) : null;
2612             if(strings)
2613             {
2614                int order = 0;
2615                Array<String> tempStrings = optionTempStrings[option];
2616                if(!tempStrings)
2617                   optionTempStrings[option] = tempStrings = { };
2618                for(s : strings)
2619                {
2620                   bool found = false;
2621                   char priorityMark[10];
2622                   order++;
2623                   if(priority)
2624                      sprintf(priorityMark, "%06d\n", priority * 1000 + order);
2625                   for(i : tempStrings; !(caseSensitive ? strcmp : strcmpi)(i, s)) { found = true; break; }
2626                   if(!found) tempStrings.Add(priority ? PrintString(priorityMark, s) : CopyString(s));
2627                }
2628             }
2629          }
2630          else
2631          {
2632             Array<String> * newStrings = (Array<String>*)((byte *)options + option);
2633             Array<String> * strings = (Array<String>*)((byte *)output + option);
2634             if(*strings) { strings->Free(); delete *strings; }
2635             if(*newStrings && newStrings->count) { *strings = { }; strings->Copy((void*)*newStrings); }
2636          }
2637       }
2638       void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2639          if(mergeValues)
2640          {
2641             Array<String> tempStrings = optionTempStrings[option];
2642             Array<String> * strings = (Array<String>*)((byte *)output + option);
2643             if(*strings) { strings->Free(); delete *strings; }
2644             if(tempStrings && tempStrings.count) { *strings = { }; strings->Copy((void*)tempStrings); }
2645             delete tempStrings;
2646          }
2647       }
2648    };
2649    GenericOptionTools<WarningsOption>       utilWarningsOption {
2650       bool OptionCheck(ProjectOptions options, int option) {
2651          WarningsOption value = *(WarningsOption*)((byte *)options + option);
2652          return value && value != none;
2653       }
2654       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2655          WarningsOption value = options ? *(WarningsOption*)((byte *)options + option) : (WarningsOption)0;
2656          *(WarningsOption*)((byte *)output + option) = value;
2657       }
2658    };
2659    GenericOptionTools<OptimizationStrategy> utilOptimizationStrategy {
2660       bool OptionCheck(ProjectOptions options, int option) {
2661          OptimizationStrategy value = *(OptimizationStrategy*)((byte *)options + option);
2662          return value && value != none;
2663       }
2664       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2665          OptimizationStrategy value = options ? *(OptimizationStrategy*)((byte *)options + option) : (OptimizationStrategy)0;
2666          *(OptimizationStrategy*)((byte *)output + option) = value;
2667       }
2668    };
2669
2670    Map<int, GenericOptionTools> ot { };
2671
2672    // The following are compiler options
2673
2674    ot[OPTION(debug)] =                   utilSetBool;
2675    ot[OPTION(memoryGuard)] =             utilSetBool;
2676    ot[OPTION(profile)] =                 utilSetBool;
2677    ot[OPTION(noLineNumbers)] =           utilSetBool;
2678    ot[OPTION(strictNameSpaces)] =        utilSetBool;
2679    ot[OPTION(fastMath)] =                utilSetBool;
2680
2681    ot[OPTION(defaultNameSpace)] =        utilString;
2682
2683    ot[OPTION(preprocessorDefinitions)] = utilStringArrays;
2684    ot[OPTION(includeDirs)] =             utilStringArrays;
2685
2686    ot[OPTION(warnings)] =                utilWarningsOption;
2687
2688    ot[OPTION(optimization)] =            utilOptimizationStrategy;
2689
2690    for(n = node; n; n = n.parent)
2691    {
2692       ProjectConfig nodeConfig = null;
2693       if(n.parent)
2694          priority = (priority / 10 + 1) * 10;
2695       else
2696          priority = 9990;
2697       if(projectConfig && n.configurations)
2698       {
2699          for(c : n.configurations; !strcmpi(c.name, projectConfig.name))
2700          {
2701             if(platform && c.platforms)
2702             {
2703                for(p : c.platforms; !strcmpi(p.name, platformName))
2704                {
2705                   for(uu : ot)
2706                   {
2707                      GenericOptionTools u = uu;
2708                      o = &uu;
2709                      if(!optionDone[o] && p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
2710                      {
2711                         u.LoadOption(p.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
2712                         if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2713                         optionConfigXplatformSet[o] = true;
2714                      }
2715                   }
2716                   break;
2717                }
2718             }
2719             nodeConfig = c;
2720             break;
2721          }
2722       }
2723       for(uu : ot)
2724       {
2725          GenericOptionTools u = uu;
2726          o = &uu;
2727          if(!optionDone[o])
2728          {
2729             if(platform && n.platforms && (!optionConfigXplatformSet[o] || !u.configReplaces))
2730             {
2731                for(p : n.platforms; !strcmpi(p.name, platformName))
2732                {
2733                   if(p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
2734                   {
2735                      u.LoadOption(p.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2736                      if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2737                   }
2738                   break;
2739                }
2740             }
2741             if(!optionDone[o] && nodeConfig && nodeConfig.options &&
2742                   ((u.mergeValues && !u.configReplaces) ?
2743                         u.OptionCheck(nodeConfig.options, o) :
2744                         u.OptionSet(nodeConfig.options, o)))
2745             {
2746                u.LoadOption(nodeConfig.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
2747                if(!u.mergeValues || u.configReplaces) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2748             }
2749             if(!optionDone[o])
2750             {
2751                if(n.options && (u.mergeValues ? u.OptionCheck(n.options, o) : u.OptionSet(n.options, o)))
2752                {
2753                   u.LoadOption(n.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2754                   if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2755                }
2756                else if(!n.parent)
2757                {
2758                   u.LoadOption(null, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2759                   if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2760                }
2761             }
2762          }
2763       }
2764    }
2765    for(uu : ot)
2766    {
2767       GenericOptionTools u = uu;
2768       o = &uu;
2769       if(!optionDone[o])
2770          u.FinalizeLoading(o, optionTempStrings, output);
2771    }
2772
2773    delete optionConfigXplatformSet;
2774    delete optionDone;
2775    delete optionTempStrings;
2776
2777    delete utilSetBool;
2778    delete utilString;
2779    delete utilStringArrays;
2780    delete utilWarningsOption;
2781    delete utilOptimizationStrategy;
2782
2783    delete ot;
2784
2785    return output;
2786 }
2787
2788 static void CollectPlatformsCommonOptions(Map<Platform, ProjectOptions> byPlatformOptions, ProjectOptions * platformsCommonOptions)
2789 {
2790    char * s;
2791    int i;
2792    ProjectOptions first;
2793    ProjectOptions commonOptions;
2794
2795    Map<String, int> countIncludeDirs { };
2796    Map<String, int> countPreprocessorDefinitions { };
2797    Map<String, bool> commonIncludeDirs { };
2798    Map<String, bool> commonPreprocessorDefinitions { };
2799
2800    for(options : byPlatformOptions) { first = options; break; }
2801
2802    *platformsCommonOptions = commonOptions = first.Copy();
2803
2804    if(commonOptions.includeDirs)
2805       commonOptions.includeDirs.Free();
2806    if(commonOptions.preprocessorDefinitions)
2807       commonOptions.preprocessorDefinitions.Free();
2808
2809    for(options : byPlatformOptions)
2810    {
2811       if(options != first)
2812       {
2813          if(commonOptions.debug && options.debug != commonOptions.debug)
2814             commonOptions.debug = unset;
2815          if(commonOptions.memoryGuard && options.memoryGuard != commonOptions.memoryGuard)
2816             commonOptions.memoryGuard = unset;
2817          if(commonOptions.profile && options.profile != commonOptions.profile)
2818             commonOptions.profile = unset;
2819          if(commonOptions.noLineNumbers && options.noLineNumbers != commonOptions.noLineNumbers)
2820             commonOptions.noLineNumbers = unset;
2821          if(commonOptions.strictNameSpaces && options.strictNameSpaces != commonOptions.strictNameSpaces)
2822             commonOptions.strictNameSpaces = unset;
2823          if(commonOptions.fastMath && options.fastMath != commonOptions.fastMath)
2824             commonOptions.fastMath = unset;
2825
2826          if(commonOptions.warnings && options.warnings != commonOptions.warnings)
2827             commonOptions.warnings = unset;
2828          if(commonOptions.optimization && options.optimization != commonOptions.optimization)
2829             commonOptions.optimization = unset;
2830
2831          if(commonOptions.defaultNameSpace && strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
2832             delete commonOptions.defaultNameSpace;
2833       }
2834
2835       CountSameNonEmptyOrNullStrings(options.includeDirs, countIncludeDirs);
2836       CountSameNonEmptyOrNullStrings(options.preprocessorDefinitions, countPreprocessorDefinitions);
2837    }
2838
2839    GetPlatformsCommonStrings(countIncludeDirs, byPlatformOptions.count,
2840          commonIncludeDirs, commonOptions.includeDirs);
2841    GetPlatformsCommonStrings(countPreprocessorDefinitions, byPlatformOptions.count,
2842          commonPreprocessorDefinitions, commonOptions.preprocessorDefinitions);
2843
2844    for(options : byPlatformOptions)
2845    {
2846       if(options.debug && options.debug == commonOptions.debug)
2847          options.debug = unset;
2848       if(options.memoryGuard && options.memoryGuard == commonOptions.memoryGuard)
2849          options.memoryGuard = unset;
2850       if(options.profile && options.profile == commonOptions.profile)
2851          options.profile = unset;
2852       if(options.noLineNumbers && options.noLineNumbers == commonOptions.noLineNumbers)
2853          options.noLineNumbers = unset;
2854       if(options.strictNameSpaces && options.strictNameSpaces == commonOptions.strictNameSpaces)
2855          options.strictNameSpaces = unset;
2856       if(options.fastMath && options.fastMath == commonOptions.fastMath)
2857          options.fastMath = unset;
2858
2859       if(options.warnings && options.warnings == commonOptions.warnings)
2860          options.warnings = unset;
2861       if(options.optimization && options.optimization == commonOptions.optimization)
2862          options.optimization = unset;
2863
2864       if(options.defaultNameSpace && !strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
2865          delete options.defaultNameSpace;
2866
2867       RemovePlatformsCommonStrings(commonIncludeDirs, options.includeDirs);
2868       RemovePlatformsCommonStrings(commonPreprocessorDefinitions, options.preprocessorDefinitions);
2869    }
2870
2871    delete countIncludeDirs;
2872    delete countPreprocessorDefinitions;
2873    delete commonIncludeDirs;
2874    delete commonPreprocessorDefinitions;
2875 }
2876
2877 static ComplexComparison PlatformsOptionsGreaterEqual(Map<Platform, ProjectOptions> byPlatformOptions,
2878       Map<Platform, ProjectOptions> parentByPlatformOptions,
2879       Map<Platform, ProjectOptions> additionsByPlatformOptions)
2880 {
2881    ComplexComparison result = equal;
2882    ComplexComparison compare;
2883    Platform platform;
2884    for(platform = (Platform)0; platform < Platform::enumSize; platform++)
2885    {
2886       ProjectOptions additionalOptions;
2887       additionsByPlatformOptions[platform] = { };
2888       additionalOptions = additionsByPlatformOptions[platform];
2889       compare = ExtractPlatformsOptionsAdditions(byPlatformOptions[platform], parentByPlatformOptions[platform], additionalOptions);
2890       if(compare == greater && result == equal)
2891          result = greater;
2892       else if(compare == different)
2893       {
2894          result = different;
2895          break;
2896       }
2897    }
2898    return result;
2899 }
2900
2901 static ComplexComparison ExtractPlatformsOptionsAdditions(ProjectOptions options, ProjectOptions parentOptions, ProjectOptions additionalOptions)
2902 {
2903    ComplexComparison result = equal;
2904    if(options.debug != parentOptions.debug ||
2905          options.memoryGuard != parentOptions.memoryGuard ||
2906          options.profile != parentOptions.profile ||
2907          options.noLineNumbers != parentOptions.noLineNumbers ||
2908          options.strictNameSpaces != parentOptions.strictNameSpaces ||
2909          options.fastMath != parentOptions.fastMath ||
2910          options.warnings != parentOptions.warnings ||
2911          options.optimization != parentOptions.optimization ||
2912          (options.defaultNameSpace != parentOptions.defaultNameSpace &&
2913                strcmp(options.defaultNameSpace, parentOptions.defaultNameSpace)))
2914       result = different;
2915    else
2916    {
2917       if(!StringsAreSameOrMore(options.includeDirs, parentOptions.includeDirs, &additionalOptions.includeDirs) ||
2918             !StringsAreSameOrMore(options.preprocessorDefinitions, parentOptions.preprocessorDefinitions, &additionalOptions.preprocessorDefinitions))
2919          result = different;
2920       if((additionalOptions.includeDirs && additionalOptions.includeDirs.count) ||
2921             (additionalOptions.preprocessorDefinitions && additionalOptions.preprocessorDefinitions.count))
2922          result = greater;
2923    }
2924    return result;
2925 }
2926
2927 enum ComplexComparison { different/*, smaller*/, equal, greater };
2928
2929 static bool StringsAreSameOrMore(Array<String> strings, Array<String> originals, Array<String> * additions)
2930 {
2931    bool result = true;
2932    if((!strings || !strings.count) && originals && originals.count)
2933       result = false;
2934    else if(strings && strings.count && (!originals || !originals.count))
2935    {
2936       if(!*additions)
2937          *additions = { };
2938       for(s : strings)
2939          additions->Add(CopyString(s));
2940    }
2941    else if(strings && strings.count && originals && originals.count)
2942    {
2943       Map<String, String> map { };
2944       MapIterator<String, bool> mit { map = map };
2945       for(it : strings)
2946       {
2947          char * s = strstr(it, "\n");
2948          s = s ? s+1 : it;
2949          map[s] = it;
2950       }
2951       for(it : originals)
2952       {
2953          char * s = strstr(it, "\n");
2954          s = s ? s+1 : it;
2955          if(!mit.Index(s, false))
2956          {
2957             result = false;
2958             break;
2959          }
2960          else
2961             map[s] = null;
2962       }
2963       if(result)
2964       {
2965          if(!*additions)
2966             *additions = { };
2967          for(it : map)
2968          {
2969             if(it)
2970                additions->Add(CopyString(it));
2971          }
2972       }
2973       delete map;
2974    }
2975    return result;
2976 }
2977
2978 static void CountSameNonEmptyOrNullStrings(Array<String> strings, Map<String, int> counts)
2979 {
2980    if(strings)
2981    {
2982       for(it : strings)
2983       {
2984          char * s = it;
2985          if(s && s[0])
2986             counts[s]++;
2987       }
2988    }
2989 }
2990
2991 static void GetPlatformsCommonStrings(Map<String, int> counts, int goodCount, Map<String, bool> common, Array<String> strings)
2992 {
2993    for(it : counts)
2994    {
2995       int i = it;
2996       if(i == goodCount)
2997       {
2998          char * s = &it;
2999          strings.Add(CopyString(s));
3000          common[s] = true;
3001       }
3002    }
3003 }
3004
3005 static void RemovePlatformsCommonStrings(Map<String, bool> common, Array<String> strings)
3006 {
3007    if(strings)
3008    {
3009       Array<String> tmp { };
3010       MapIterator<String, bool> mit { map = common };
3011       for(it : strings)
3012       {
3013          char * s = it;
3014          if(!mit.Index(s, false))
3015             tmp.Add(CopyString(s));
3016       }
3017       strings.Free();
3018       if(tmp.count)
3019       {
3020          for(s : tmp)
3021             strings.Add(CopyString(s));
3022          tmp.Free();
3023       }
3024       delete tmp;
3025    }
3026 }
3027
3028 static void GenMakePrintNodeFlagsVariable(ProjectNode node, Map<intptr, int> nodeFlagsMapping, String variableName, File f)
3029 {
3030    int customFlags;
3031    customFlags = nodeFlagsMapping[(intptr)node];
3032    if(customFlags > 1)
3033       f.Printf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
3034    else
3035       f.Printf(" $(%s)", variableName);
3036 }
3037
3038 static void DynStringPrintNodeFlagsVariable(ProjectNode node, Map<intptr, int> nodeFlagsMapping, String variableName, DynamicString s)
3039 {
3040    int customFlags;
3041    customFlags = nodeFlagsMapping[(intptr)node];
3042    if(customFlags > 1)
3043       s.concatf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
3044    else
3045       s.concatf(" $(%s)", variableName);
3046 }
3047
3048 static void GenCFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, bool commonOptions, bool isGreater, DynamicString s)
3049 {
3050    if(!isGreater)
3051    {
3052       //if(gccCompiler)
3053       {
3054          if(options.optimization == speed || options.optimization == size ||
3055                options.fastMath == true || options.debug == true)
3056          {
3057             if(options.debug != true)
3058             {
3059                s.concat(" $(if $(DEBUG),");
3060                s.concat(" -g");
3061                s.concat(",");
3062             }
3063             switch(options.optimization)
3064             {
3065                case speed: s.concat(" -O2"); break;
3066                case size: s.concat(" -Os"); break;
3067             }
3068             if(options.fastMath == true)
3069                s.concat(" -ffast-math");
3070             if(options.debug == true)
3071                s.concat(" -g");
3072             if(options.debug != true)
3073                s.concat(")");
3074          }
3075          else if(commonOptions)
3076             s.concat(" $(if $(DEBUG),-g)");
3077          if(commonOptions)
3078             s.concat(" $(FPIC)");
3079       }
3080       switch(options.warnings)
3081       {
3082          case all: s.concat(" -Wall"); break;
3083          case none: s.concat(" -w"); break;
3084       }
3085       if(options.profile)
3086          s.concat(" -pg");
3087    }
3088
3089    if(options && options.preprocessorDefinitions)
3090       ListOptionToDynamicString(s, _D, options.preprocessorDefinitions, false, lineEach, "\t\t\t");
3091    if(options && options.includeDirs)
3092       ListOptionToDynamicString(s, _I, options.includeDirs, true, lineEach, "\t\t\t");
3093 }
3094
3095 static void GenECFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, DynamicString s)
3096 {
3097    if(options.memoryGuard == true)
3098       s.concat(" -memguard");
3099    if(options.noLineNumbers == true)
3100       s.concat(" -nolinenumbers");
3101    if(options.strictNameSpaces == true)
3102       s.concat(" -strictns");
3103    if(options.defaultNameSpace && options.defaultNameSpace[0])
3104       s.concatf(" -defaultns %s", options.defaultNameSpace);
3105 }
3106
3107 static void ListOptionToDynamicString(DynamicString output, ToolchainFlag flag, Array<String> list, bool prioritize,
3108       LineOutputMethod lineMethod, String newLineStart)
3109 {
3110    if(list.count)
3111    {
3112       if(lineMethod == newLine)
3113       {
3114          output.concat(" \\\n");
3115          output.concat(newLineStart);
3116       }
3117       if(prioritize)
3118       {
3119          Map<String, int> sortedList { };
3120          MapNode<String, int> mn;
3121          for(item : list)
3122             sortedList[item] = 1;
3123          for(mn = sortedList.root.minimum; mn; mn = mn.next)
3124          {
3125             char * start = strstr(mn.key, "\n");
3126             if(lineMethod == lineEach)
3127             {
3128                output.concat(" \\\n");
3129                output.concat(newLineStart);
3130             }
3131             output.concat(" ");
3132             output.concat(flagNames[flag]);
3133             EscapeForMakeToDynString(output, start ? start+1 : mn.key, false, true, flag == _D);
3134          }
3135          delete sortedList;
3136       }
3137       else
3138       {
3139          for(item : list)
3140          {
3141             if(lineMethod == lineEach)
3142             {
3143                output.concat(" \\\n");
3144                output.concat(newLineStart);
3145             }
3146             output.concat(" ");
3147             output.concat(flagNames[flag]);
3148             EscapeForMakeToDynString(output, item, false, true, flag == _D);
3149          }
3150       }
3151    }
3152 }
3153
3154 class GenericOptionTools<class X>
3155 {
3156    bool mergeValues, configReplaces;
3157
3158    virtual bool OptionSet(ProjectOptions options, int option) {
3159       if(*(X*)((byte *)options + option))
3160          return true;
3161       return false;
3162    }
3163
3164    // BUG: OptionCheck = OptionSet; // Overrides derived classes OptionCheck ?
3165
3166    virtual bool OptionCheck(ProjectOptions options, int option) {
3167       return OptionSet(options, option);
3168    }
3169
3170    virtual void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output);
3171    virtual void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output);
3172 }
3173
3174 class StringArrayOptionTools : GenericOptionTools<Array<String>>
3175 {
3176    bool caseSensitive;
3177 }
3178
3179 class NameCollisionInfo
3180 {
3181    bool ec;
3182    bool s;
3183    bool c;
3184    bool rc;
3185    bool cpp;
3186    bool cc;
3187    bool cxx;
3188    bool m;
3189    bool mm;
3190    byte count;
3191
3192    bool IsExtensionColliding(char * extension)
3193    {
3194       bool colliding;
3195       if(count > 1 &&
3196             ((!strcmpi(extension, "c")   && ec) ||
3197              (!strcmpi(extension, "rc")  && (ec || c)) ||
3198              (!strcmpi(extension, "s")   && (ec || c || rc)) ||
3199              (!strcmpi(extension, "cpp") && (ec || c || rc || s)) ||
3200              (!strcmpi(extension, "cc")  && (ec || c || rc || s || cpp)) ||
3201              (!strcmpi(extension, "cxx") && (ec || c || rc || s || cpp || cc)) ||
3202              (!strcmpi(extension, "m")   && (ec || c || rc || s || cpp || cc || m)) ||
3203               !strcmpi(extension, "mm")))
3204          colliding = true;
3205       else
3206          colliding = false;
3207      return colliding;
3208    }
3209 }