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