ide; added support for compiling whole folders. improvement over existing support...
[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                   cflags.concatf(isGreater ? "%s" : " \\\n\t%s", (String)s);
2150                delete s;
2151                s = { };
2152                GenECFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, s);
2153                if(s.count > 1)
2154                   ecflags.concatf(" \\\n\t%s", (String)s);
2155                delete s;
2156
2157                if(isGreater)
2158                {
2159                   cflags.concatf(" \\\n\t");
2160                   DynStringPrintNodeFlagsVariable(parent, nodeCFlagsMapping, "PRJ_CFLAGS", cflags);
2161                }
2162             }
2163
2164             additionsByPlatformOptions.Free();
2165             delete additionsByPlatformOptions;
2166
2167             // output
2168             {
2169                if(isEqual)
2170                {
2171                   nodeCFlagsMapping[(int)this] = nodeCFlagsMapping[(int)parent];
2172                   nodeECFlagsMapping[(int)this] = nodeECFlagsMapping[(int)parent];
2173                }
2174                else
2175                {
2176                   String s;
2177                   int variationNum;
2178
2179                   variationNum = 1;
2180                   if((s = cflags) && s[0] && !(variationNum = cflagsVariations[s]))
2181                      cflagsVariations[s] = variationNum = cflagsVariations.count;
2182                   nodeCFlagsMapping[(int)this] = variationNum;
2183
2184                   variationNum = 1;
2185                   if((s = ecflags) && s[0] && !(variationNum = ecflagsVariations[s]))
2186                      ecflagsVariations[s] = variationNum = ecflagsVariations.count;
2187                   nodeECFlagsMapping[(int)this] = variationNum;
2188                }
2189             }
2190
2191             delete cflags;
2192             delete ecflags;
2193          }
2194          else
2195          {
2196             // output
2197             {
2198                nodeCFlagsMapping[(int)this] = nodeCFlagsMapping[(int)parent];
2199                nodeECFlagsMapping[(int)this] = nodeECFlagsMapping[(int)parent];
2200             }
2201          }
2202
2203       }
2204       if(files)
2205       {
2206          for(child : files)
2207          {
2208             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2209                child.GenMakeCollectAssignNodeFlags(prjConfig, prjWithEcFiles,
2210                      cflagsVariations, nodeCFlagsMapping, ecflagsVariations, nodeECFlagsMapping,
2211                      byPlatformOptions);
2212          }
2213       }
2214
2215       if(byPlatformOptions != parentByPlatformOptions)
2216       {
2217          byPlatformOptions.Free();
2218          delete byPlatformOptions;
2219       }
2220    }
2221
2222    Array<Platform> GetPlatformsArrayFromExclusionInfo(ProjectConfig prjConfig)
2223    {
2224       Array<Platform> platforms { };
2225       Map<Platform, SetBool> exclusionInfo { };
2226       CollectExclusionInfo(exclusionInfo, prjConfig);
2227
2228       if(exclusionInfo[unknown] == true)
2229       {
2230          if(exclusionInfo.count > 1)
2231             for(p : exclusionInfo; p == false)
2232                platforms.Add(&p);
2233       }
2234       else
2235       {
2236          bool onlyOnknown = true;
2237          for(p : exclusionInfo)
2238             if(&p != unknown && p == true)
2239             {
2240                onlyOnknown = false;
2241                break;
2242             }
2243          if(onlyOnknown)
2244             platforms.Add(unknown);
2245          else
2246          {
2247             Platform p;
2248             for(p = unknown + 1; p < Platform::enumSize; p++)
2249                if(exclusionInfo[p] != true)
2250                   platforms.Add(p);
2251          }
2252       }
2253       delete exclusionInfo;
2254       return platforms;
2255    }
2256
2257    void GetTargets(ProjectConfig prjConfig, char * objDir, DynamicString output)
2258    {
2259       if(type == file)
2260       {
2261          output.concat(" \"");
2262          output.concat(objDir);
2263          output.concat("/");
2264          {
2265             char fileName[MAX_FILENAME];
2266             strcpy(fileName, name);
2267             // 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.
2268             ChangeExtension(fileName, "o", fileName);
2269             output.concat(fileName);
2270          }
2271          output.concat("\"");
2272       }
2273       else if(files)
2274       {
2275          for(child : files)
2276          {
2277             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2278                child.GetTargets(prjConfig, objDir, output);
2279          }
2280       }
2281    }
2282
2283    void DeleteIntermediateFiles(CompilerConfig compiler, ProjectConfig prjConfig)
2284    {
2285       if(type == file)
2286       {
2287          char fileName[MAX_FILENAME];
2288          char extension[MAX_EXTENSION];
2289          Project prj = property::project;
2290          DirExpression objDir = prj.GetObjDir(compiler, prjConfig);
2291
2292          strcpy(fileName, prj.topNode.path);
2293          PathCatSlash(fileName, objDir.dir);
2294          PathCatSlash(fileName, name);
2295
2296          // TODO/NOTE: this will not delete file.c.o when whe have both a file.c and a file.ec in a project.
2297          ChangeExtension(fileName, "o", fileName);
2298          if(FileExists(fileName))
2299             DeleteFile(fileName);
2300
2301          GetExtension(name, extension);
2302          if(!strcmp(extension, "ec"))
2303          {
2304             ChangeExtension(fileName, "c", fileName);
2305             if(FileExists(fileName))
2306                DeleteFile(fileName);
2307
2308             ChangeExtension(fileName, "sym", fileName);
2309             if(FileExists(fileName))
2310                DeleteFile(fileName);
2311
2312             ChangeExtension(fileName, "imp", fileName);
2313             if(FileExists(fileName))
2314                DeleteFile(fileName);
2315
2316             ChangeExtension(fileName, "bowl", fileName);
2317             if(FileExists(fileName))
2318                DeleteFile(fileName);
2319          }
2320
2321          delete objDir;
2322       }
2323       else if(files)
2324       {
2325          for(child : files)
2326          {
2327             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2328                child.DeleteIntermediateFiles(compiler, prjConfig);
2329          }
2330       }
2331    }
2332
2333    bool IsInNode(ProjectNode node)
2334    {
2335       bool result = false;
2336       ProjectNode n;
2337       for(n = this; n; n = n.parent)
2338       {
2339          if(n == node)
2340          {
2341             result = true;
2342             break;
2343          }
2344       }
2345       return result;
2346    }
2347 }
2348
2349 // the code in this function is closely matched to OptionsBox::Load
2350 // and accompanying derivations of OptionBox and their use of OptionSet,
2351 // OptionCheck, LoadOption and FinalizeLoading methods.
2352 // output changing modification should be mirrored in both implementations
2353 static ProjectOptions BlendFileConfigPlatformProjectOptions(ProjectNode node, ProjectConfig projectConfig, Platform platform)
2354 {
2355    ProjectOptions output { };
2356
2357    // legend: e Element
2358    //         o Option (of a ProjectOptions)
2359    //         n Node (ProjectNode)
2360    //         p Platform
2361    //         u Utility (GenericOptionTools)
2362
2363    int e;
2364    int o;
2365    int priority = 0;
2366    int includeDirsOption = OPTION(includeDirs);
2367    ProjectNode n;
2368    char * platformName = platform ? platform.OnGetString(0,0,0) : null;
2369
2370    Array<bool> optionConfigXplatformSet   { size = OPTION(postbuildCommands) };
2371    Array<bool> optionDone                 { size = OPTION(postbuildCommands) };
2372    Array<Array<String>> optionTempStrings { size = OPTION(postbuildCommands) };
2373
2374    GenericOptionTools<SetBool>              utilSetBool {
2375       bool OptionCheck(ProjectOptions options, int option) {
2376          return *(SetBool*)((byte *)options + option) == true;
2377       }
2378       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2379          if(options && (*(SetBool*)((byte *)options + option) == true))
2380             *(SetBool*)((byte *)output + option) = true;
2381       }
2382    };
2383    GenericOptionTools<String>               utilString {
2384       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2385          String * string = (String*)((byte *)output + option);
2386          if(*string) delete *string;
2387          if(options)
2388             *string = CopyString(*(String*)((byte *)options + option));
2389       }
2390    };
2391    StringArrayOptionTools                   utilStringArrays {
2392       mergeValues = true;
2393       caseSensitive = true;
2394       bool OptionCheck(ProjectOptions options, int option) {
2395          String string = *(String*)((byte *)options + option);
2396          return string && string[0];
2397       }
2398       bool OptionSet(ProjectOptions options, int option) {
2399          Array<String> strings = *(Array<String>*)((byte *)options + option);
2400          if(mergeValues && !configReplaces)
2401             return strings && strings.count;
2402          else
2403             return strings != null;
2404       }
2405       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2406          if(mergeValues)
2407          {
2408             Array<String> strings = options ? *((Array<String>*)((byte *)options + option) : null;
2409             if(strings)
2410             {
2411                int order = 0;
2412                Array<String> tempStrings = optionTempStrings[option];
2413                if(!tempStrings)
2414                   optionTempStrings[option] = tempStrings = { };
2415                for(s : strings)
2416                {
2417                   bool found = false;
2418                   char priorityMark[10];
2419                   order++;
2420                   if(priority)
2421                      sprintf(priorityMark, "%06d\n", priority * 1000 + order);
2422                   for(i : tempStrings; !(caseSensitive ? strcmp : strcmpi)(i, s)) { found = true; break; }
2423                   if(!found) tempStrings.Add(priority ? PrintString(priorityMark, s) : CopyString(s));
2424                }
2425             }
2426          }
2427          else
2428          {
2429             Array<String> * newStrings = (Array<String>*)((byte *)options + option);
2430             Array<String> * strings = (Array<String>*)((byte *)output + option);
2431             if(*strings) { strings->Free(); delete *strings; }
2432             if(*newStrings && newStrings->count) { *strings = { }; strings->Copy((void*)*newStrings); }
2433          }
2434       }
2435       void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2436          if(mergeValues)
2437          {
2438             Array<String> tempStrings = optionTempStrings[option];
2439             Array<String> * strings = (Array<String>*)((byte *)output + option);
2440             if(*strings) { strings->Free(); delete *strings; }
2441             if(tempStrings && tempStrings.count) { *strings = { }; strings->Copy((void*)tempStrings); }
2442             delete tempStrings;
2443          }
2444       }
2445    };
2446    GenericOptionTools<WarningsOption>       utilWarningsOption {
2447       bool OptionCheck(ProjectOptions options, int option) {
2448          WarningsOption value = *(WarningsOption*)((byte *)options + option);
2449          return value && value != none;
2450       }
2451       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2452          WarningsOption value = options ? *(WarningsOption*)((byte *)options + option) : (WarningsOption)0;
2453          *(WarningsOption*)((byte *)output + option) = value;
2454       }
2455    };
2456    GenericOptionTools<OptimizationStrategy> utilOptimizationStrategy {
2457       bool OptionCheck(ProjectOptions options, int option) {
2458          OptimizationStrategy value = *(OptimizationStrategy*)((byte *)options + option);
2459          return value && value != none;
2460       }
2461       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2462          OptimizationStrategy value = options ? *(OptimizationStrategy*)((byte *)options + option) : (OptimizationStrategy)0;
2463          *(OptimizationStrategy*)((byte *)output + option) = value;
2464       }
2465    };
2466    GenericOptionTools<BuildBitDepth>        utilBuildBitDepth {
2467       bool OptionCheck(ProjectOptions options, int option) {
2468          BuildBitDepth value = *(BuildBitDepth*)((byte *)options + option);
2469          return value && value != all;
2470       }
2471       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2472          BuildBitDepth value = options ? *(BuildBitDepth*)((byte *)options + option) : (BuildBitDepth)0;
2473          *(BuildBitDepth*)((byte *)output + option) = value;
2474       }
2475    };
2476
2477    Map<int, GenericOptionTools> ot { };
2478
2479    // The following are compiler options
2480
2481    ot[OPTION(debug)] =                   utilSetBool;
2482    ot[OPTION(memoryGuard)] =             utilSetBool;
2483    ot[OPTION(profile)] =                 utilSetBool;
2484    ot[OPTION(noLineNumbers)] =           utilSetBool;
2485    ot[OPTION(strictNameSpaces)] =        utilSetBool;
2486    ot[OPTION(fastMath)] =                utilSetBool;
2487
2488    ot[OPTION(defaultNameSpace)] =        utilString;
2489
2490    ot[OPTION(preprocessorDefinitions)] = utilStringArrays;
2491    ot[OPTION(includeDirs)] =             utilStringArrays;
2492
2493    ot[OPTION(warnings)] =                utilWarningsOption;
2494
2495    ot[OPTION(optimization)] =            utilOptimizationStrategy;
2496
2497    ot[OPTION(buildBitDepth)] =           utilBuildBitDepth;
2498
2499    for(n = node; n; n = n.parent)
2500    {
2501       ProjectConfig nodeConfig = null;
2502       if(n.parent)
2503          priority = (priority / 10 + 1) * 10;
2504       else
2505          priority = 9990;
2506       if(projectConfig && n.configurations)
2507       {
2508          for(c : n.configurations; !strcmpi(c.name, projectConfig.name))
2509          {
2510             if(platform && c.platforms)
2511             {
2512                for(p : c.platforms; !strcmpi(p.name, platformName))
2513                {
2514                   for(uu : ot)
2515                   {
2516                      GenericOptionTools u = uu;
2517                      o = &uu;
2518                      if(!optionDone[o] && p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
2519                      {
2520                         u.LoadOption(p.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
2521                         if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2522                         optionConfigXplatformSet[o] = true;
2523                      }
2524                   }
2525                   break;
2526                }
2527             }
2528             nodeConfig = c;
2529             break;
2530          }
2531       }
2532       for(uu : ot)
2533       {
2534          GenericOptionTools u = uu;
2535          o = &uu;
2536          if(!optionDone[o])
2537          {
2538             if(platform && n.platforms && (!optionConfigXplatformSet[o] || !u.configReplaces))
2539             {
2540                for(p : n.platforms; !strcmpi(p.name, platformName))
2541                {
2542                   if(p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
2543                   {
2544                      u.LoadOption(p.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2545                      if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2546                   }
2547                   break;
2548                }
2549             }
2550             if(!optionDone[o] && nodeConfig && nodeConfig.options &&
2551                   ((u.mergeValues && !u.configReplaces) ?
2552                         u.OptionCheck(nodeConfig.options, o) :
2553                         u.OptionSet(nodeConfig.options, o)))
2554             {
2555                u.LoadOption(nodeConfig.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
2556                if(!u.mergeValues || u.configReplaces) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2557             }
2558             if(!optionDone[o])
2559             {
2560                if(n.options && (u.mergeValues ? u.OptionCheck(n.options, o) : u.OptionSet(n.options, o)))
2561                {
2562                   u.LoadOption(n.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2563                   if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2564                }
2565                else if(!n.parent)
2566                {
2567                   u.LoadOption(null, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2568                   if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2569                }
2570             }
2571          }
2572       }
2573    }
2574    for(uu : ot)
2575    {
2576       GenericOptionTools u = uu;
2577       o = &uu;
2578       if(!optionDone[o])
2579          u.FinalizeLoading(o, optionTempStrings, output);
2580    }
2581
2582    delete optionConfigXplatformSet;
2583    delete optionDone;
2584    delete optionTempStrings;
2585
2586    delete utilSetBool;
2587    delete utilString;
2588    delete utilStringArrays;
2589    delete utilWarningsOption;
2590    delete utilOptimizationStrategy;
2591    delete utilBuildBitDepth;
2592
2593    delete ot;
2594
2595    return output;
2596 }
2597
2598 static void CollectPlatformsCommonOptions(Map<Platform, ProjectOptions> byPlatformOptions, ProjectOptions * platformsCommonOptions)
2599 {
2600    char * s;
2601    int i;
2602    ProjectOptions first;
2603    ProjectOptions commonOptions;
2604
2605    Map<String, int> countIncludeDirs { };
2606    Map<String, int> countPreprocessorDefinitions { };
2607    Map<String, bool> commonIncludeDirs { };
2608    Map<String, bool> commonPreprocessorDefinitions { };
2609
2610    for(options : byPlatformOptions) { first = options; break; }
2611
2612    *platformsCommonOptions = commonOptions = first.Copy();
2613
2614    if(commonOptions.includeDirs)
2615       commonOptions.includeDirs.Free();
2616    if(commonOptions.preprocessorDefinitions)
2617       commonOptions.preprocessorDefinitions.Free();
2618
2619    for(options : byPlatformOptions)
2620    {
2621       if(options != first)
2622       {
2623          if(commonOptions.debug && options.debug != commonOptions.debug)
2624             commonOptions.debug = unset;
2625          if(commonOptions.memoryGuard && options.memoryGuard != commonOptions.memoryGuard)
2626             commonOptions.memoryGuard = unset;
2627          if(commonOptions.profile && options.profile != commonOptions.profile)
2628             commonOptions.profile = unset;
2629          if(commonOptions.noLineNumbers && options.noLineNumbers != commonOptions.noLineNumbers)
2630             commonOptions.noLineNumbers = unset;
2631          if(commonOptions.strictNameSpaces && options.strictNameSpaces != commonOptions.strictNameSpaces)
2632             commonOptions.strictNameSpaces = unset;
2633          if(commonOptions.fastMath && options.fastMath != commonOptions.fastMath)
2634             commonOptions.fastMath = unset;
2635
2636          if(commonOptions.warnings && options.warnings != commonOptions.warnings)
2637             commonOptions.warnings = unset;
2638          if(commonOptions.optimization && options.optimization != commonOptions.optimization)
2639             commonOptions.optimization = unset;
2640          if(commonOptions.buildBitDepth && options.buildBitDepth != commonOptions.buildBitDepth)
2641             commonOptions.buildBitDepth = all;
2642
2643          if(commonOptions.defaultNameSpace && strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
2644             delete commonOptions.defaultNameSpace;
2645       }
2646
2647       CountSameNonEmptyOrNullStrings(options.includeDirs, countIncludeDirs);
2648       CountSameNonEmptyOrNullStrings(options.preprocessorDefinitions, countPreprocessorDefinitions);
2649    }
2650
2651    GetPlatformsCommonStrings(countIncludeDirs, byPlatformOptions.count,
2652          commonIncludeDirs, commonOptions.includeDirs);
2653    GetPlatformsCommonStrings(countPreprocessorDefinitions, byPlatformOptions.count,
2654          commonPreprocessorDefinitions, commonOptions.preprocessorDefinitions);
2655
2656    for(options : byPlatformOptions)
2657    {
2658       if(options.debug && options.debug == commonOptions.debug)
2659          options.debug = unset;
2660       if(options.memoryGuard && options.memoryGuard == commonOptions.memoryGuard)
2661          options.memoryGuard = unset;
2662       if(options.profile && options.profile == commonOptions.profile)
2663          options.profile = unset;
2664       if(options.noLineNumbers && options.noLineNumbers == commonOptions.noLineNumbers)
2665          options.noLineNumbers = unset;
2666       if(options.strictNameSpaces && options.strictNameSpaces == commonOptions.strictNameSpaces)
2667          options.strictNameSpaces = unset;
2668       if(options.fastMath && options.fastMath == commonOptions.fastMath)
2669          options.fastMath = unset;
2670
2671       if(options.warnings && options.warnings == commonOptions.warnings)
2672          options.warnings = unset;
2673       if(options.optimization && options.optimization == commonOptions.optimization)
2674          options.optimization = unset;
2675       if(options.buildBitDepth && options.buildBitDepth == commonOptions.buildBitDepth)
2676          options.buildBitDepth = all;
2677
2678       if(options.defaultNameSpace && !strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
2679          delete options.defaultNameSpace;
2680
2681       RemovePlatformsCommonStrings(commonIncludeDirs, options.includeDirs);
2682       RemovePlatformsCommonStrings(commonPreprocessorDefinitions, options.preprocessorDefinitions);
2683    }
2684
2685    delete countIncludeDirs;
2686    delete countPreprocessorDefinitions;
2687    delete commonIncludeDirs;
2688    delete commonPreprocessorDefinitions;
2689 }
2690
2691 static ComplexComparison PlatformsOptionsGreaterEqual(Map<Platform, ProjectOptions> byPlatformOptions,
2692       Map<Platform, ProjectOptions> parentByPlatformOptions,
2693       Map<Platform, ProjectOptions> additionsByPlatformOptions)
2694 {
2695    ComplexComparison result = equal;
2696    ComplexComparison compare;
2697    Platform platform;
2698    for(platform = (Platform)0; platform < Platform::enumSize; platform++)
2699    {
2700       ProjectOptions additionalOptions;
2701       additionsByPlatformOptions[platform] = { };
2702       additionalOptions = additionsByPlatformOptions[platform];
2703       compare = ExtractPlatformsOptionsAdditions(byPlatformOptions[platform], parentByPlatformOptions[platform], additionalOptions);
2704       if(compare == greater && result == equal)
2705          result = greater;
2706       else if(compare == different)
2707       {
2708          result = different;
2709          break;
2710       }
2711    }
2712    return result;
2713 }
2714
2715 static ComplexComparison ExtractPlatformsOptionsAdditions(ProjectOptions options, ProjectOptions parentOptions, ProjectOptions additionalOptions)
2716 {
2717    ComplexComparison result = equal;
2718    if(options.debug != parentOptions.debug ||
2719          options.memoryGuard != parentOptions.memoryGuard ||
2720          options.profile != parentOptions.profile ||
2721          options.noLineNumbers != parentOptions.noLineNumbers ||
2722          options.strictNameSpaces != parentOptions.strictNameSpaces ||
2723          options.fastMath != parentOptions.fastMath ||
2724          options.warnings != parentOptions.warnings ||
2725          options.optimization != parentOptions.optimization ||
2726          (options.defaultNameSpace != parentOptions.defaultNameSpace &&
2727                strcmp(options.defaultNameSpace, parentOptions.defaultNameSpace)))
2728       result = different;
2729    else
2730    {
2731       if(!StringsAreSameOrMore(options.includeDirs, parentOptions.includeDirs, &additionalOptions.includeDirs) ||
2732             !StringsAreSameOrMore(options.preprocessorDefinitions, parentOptions.preprocessorDefinitions, &additionalOptions.preprocessorDefinitions))
2733          result = different;
2734       if((additionalOptions.includeDirs && additionalOptions.includeDirs.count) ||
2735             (additionalOptions.preprocessorDefinitions && additionalOptions.preprocessorDefinitions.count))
2736          result = greater;
2737    }
2738    return result;
2739 }
2740
2741 enum ComplexComparison { different/*, smaller*/, equal, greater };
2742
2743 static bool StringsAreSameOrMore(Array<String> strings, Array<String> originals, Array<String> * additions)
2744 {
2745    bool result = true;
2746    if((!strings || !strings.count) && originals && originals.count)
2747       result = false;
2748    else if(strings && strings.count && (!originals || !originals.count))
2749    {
2750       if(!*additions)
2751          *additions = { };
2752       for(s : strings)
2753          additions->Add(CopyString(s));
2754    }
2755    else if(strings && strings.count && originals && originals.count)
2756    {
2757       Map<String, String> map { };
2758       MapIterator<String, bool> mit { map = map };
2759       for(it : strings)
2760       {
2761          char * s = strstr(it, "\n");
2762          s = s ? s+1 : it;
2763          map[s] = it;
2764       }
2765       for(it : originals)
2766       {
2767          char * s = strstr(it, "\n");
2768          s = s ? s+1 : it;
2769          if(!mit.Index(s, false))
2770          {
2771             result = false;
2772             break;
2773          }
2774          else
2775             map[s] = null;
2776       }
2777       if(result)
2778       {
2779          if(!*additions)
2780             *additions = { };
2781          for(it : map)
2782          {
2783             if(it)
2784                additions->Add(CopyString(it));
2785          }
2786       }
2787       delete map;
2788    }
2789    return result;
2790 }
2791
2792 static void CountSameNonEmptyOrNullStrings(Array<String> strings, Map<String, int> counts)
2793 {
2794    if(strings)
2795    {
2796       for(it : strings)
2797       {
2798          char * s = it;
2799          if(s && s[0])
2800             counts[s]++;
2801       }
2802    }
2803 }
2804
2805 static void GetPlatformsCommonStrings(Map<String, int> counts, int goodCount, Map<String, bool> common, Array<String> strings)
2806 {
2807    for(it : counts)
2808    {
2809       int i = it;
2810       if(i == goodCount)
2811       {
2812          char * s = &it;
2813          strings.Add(CopyString(s));
2814          common[s] = true;
2815       }
2816    }
2817 }
2818
2819 static void RemovePlatformsCommonStrings(Map<String, bool> common, Array<String> strings)
2820 {
2821    if(strings)
2822    {
2823       Array<String> tmp { };
2824       MapIterator<String, bool> mit { map = common };
2825       for(it : strings)
2826       {
2827          char * s = it;
2828          if(!mit.Index(s, false))
2829             tmp.Add(CopyString(s));
2830       }
2831       strings.Free();
2832       if(tmp.count)
2833       {
2834          for(s : tmp)
2835             strings.Add(CopyString(s));
2836          tmp.Free();
2837       }
2838       delete tmp;
2839    }
2840 }
2841
2842 static void GenMakePrintNodeFlagsVariable(ProjectNode node, Map<int, int> nodeFlagsMapping, String variableName, File f)
2843 {
2844    int customFlags;
2845    customFlags = nodeFlagsMapping[(int)node];
2846    if(customFlags > 1)
2847       f.Printf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
2848    else
2849       f.Printf(" $(%s)", variableName);
2850 }
2851
2852 static void DynStringPrintNodeFlagsVariable(ProjectNode node, Map<int, int> nodeFlagsMapping, String variableName, DynamicString s)
2853 {
2854    int customFlags;
2855    customFlags = nodeFlagsMapping[(int)node];
2856    if(customFlags > 1)
2857       s.concatf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
2858    else
2859       s.concatf(" $(%s)", variableName);
2860 }
2861
2862 static void GenCFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, bool commonOptions, bool isGreater, DynamicString s)
2863 {
2864    if(!isGreater)
2865    {
2866       //if(gccCompiler)
2867       {
2868          if(options.optimization == speed || options.optimization == size ||
2869                options.fastMath == true || options.debug == true)
2870          {
2871             if(options.debug != true)
2872             {
2873                s.concatf(" $(if $(DEBUG),");
2874                s.concatf(" -g");
2875                s.concatf(",");
2876             }
2877             switch(options.optimization)
2878             {
2879                case speed: s.concatf(" -O2"); break;
2880                case size: s.concatf(" -Os"); break;
2881             }
2882             if(options.fastMath == true)
2883                s.concatf(" -ffast-math");
2884             if(options.debug == true)
2885                s.concatf(" -g");
2886             if(options.debug != true)
2887                s.concatf(")");
2888          }
2889          else if(commonOptions)
2890             s.concatf(" $(if $(DEBUG),-g)");
2891          if(options.buildBitDepth || (commonOptions && prjWithEcFiles))
2892             s.concatf(" %s", (!options || !options.buildBitDepth || options.buildBitDepth == bits32) ? "$(FORCE_32_BIT)" : "$(FORCE_64_BIT)");
2893          if(commonOptions)
2894             s.concatf(" $(FPIC)");
2895       }
2896       switch(options.warnings)
2897       {
2898          case all: s.concatf(" -Wall"); break;
2899          case none: s.concatf(" -w"); break;
2900       }
2901       if(options.profile)
2902          s.concatf(" -pg");
2903    }
2904
2905    if(options && options.preprocessorDefinitions)
2906       ListOptionToDynamicString("D", options.preprocessorDefinitions, false, lineEach, "\t\t\t", false, s);
2907    if(options && options.includeDirs)
2908       ListOptionToDynamicString("I", options.includeDirs, true, lineEach, "\t\t\t", true, s);
2909 }
2910
2911 static void GenECFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, DynamicString s)
2912 {
2913    if(options.memoryGuard == true)
2914       s.concat(" -memguard");
2915    if(options.noLineNumbers == true)
2916       s.concat(" -nolinenumbers");
2917    if(options.strictNameSpaces == true)
2918       s.concat(" -strictns");
2919    if(options.defaultNameSpace && options.defaultNameSpace[0])
2920       s.concatf(" -defaultns %s", options.defaultNameSpace);
2921 }
2922
2923 static void ListOptionToDynamicString(char * option, Array<String> list, bool prioritize,
2924       ListOutputMethod method, String newLineStart, bool noSpace, DynamicString s)
2925 {
2926    if(list.count)
2927    {
2928       if(method == newLine)
2929          s.concatf(" \\\n%s", newLineStart);
2930       if(prioritize)
2931       {
2932          Map<String, int> sortedList { };
2933          MapNode<String, int> mn;
2934          for(item : list)
2935             sortedList[item] = 1;
2936          for(mn = sortedList.root.minimum; mn; mn = mn.next)
2937          {
2938             char * start = strstr(mn.key, "\n");
2939             if(method == lineEach)
2940                s.concatf(" \\\n%s", newLineStart);
2941             s.concatf(" -%s", option);
2942             if(noSpace)
2943                StringNoSpaceToDynamicString(s, start ? start+1 : mn.key);
2944             else
2945                s.concat(start ? start+1 : mn.key);
2946          }
2947          delete sortedList;
2948       }
2949       else
2950       {
2951          for(item : list)
2952          {
2953             if(method == lineEach)
2954                s.concatf(" \\\n%s", newLineStart);
2955             s.concatf(" -%s", option);
2956             if(noSpace)
2957                StringNoSpaceToDynamicString(s, item);
2958             else
2959                s.concat(item);
2960          }
2961       }
2962    }
2963 }
2964
2965 class GenericOptionTools<class X>
2966 {
2967    bool mergeValues, configReplaces;
2968
2969    virtual bool OptionSet(ProjectOptions options, int option) {
2970       if(*(X*)((byte *)options + option))
2971          return true;
2972       return false;
2973    }
2974
2975    // BUG: OptionCheck = OptionSet; // Overrides derived classes OptionCheck ?
2976
2977    virtual bool OptionCheck(ProjectOptions options, int option) {
2978       return OptionSet(options, option);
2979    }
2980
2981    virtual void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output);
2982    virtual void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output);
2983 }
2984
2985 class StringArrayOptionTools : GenericOptionTools<Array<String>>
2986 {
2987    bool caseSensitive;
2988 }
2989
2990 class NameCollisionInfo
2991 {
2992    bool ec;
2993    bool c;
2994    bool cpp;
2995    bool cc;
2996    bool cxx;
2997    bool m;
2998    byte count;
2999
3000    bool IsExtensionColliding(char * extension)
3001    {
3002       bool colliding;
3003       if(count > 1 && ((!strcmpi(extension, "c") && ec) ||
3004             (!strcmpi(extension, "cpp") && (ec || c)) ||
3005             (!strcmpi(extension, "cc") && (ec || c || cpp)) ||
3006             (!strcmpi(extension, "cxx") && (ec || c || cpp || cc)) ||
3007             !strcmpi(extension, "m")))
3008          colliding = true;
3009       else
3010          colliding = false;
3011      return colliding;
3012    }
3013 }