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