ide; fixed Trying to compile a header file gives a confusing error. closes #549
[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, "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, "cpp"))
1309                info.cpp = true;
1310             else if(!strcmpi(extension, "cc"))
1311                info.cc = true;
1312             else if(!strcmpi(extension, "cxx"))
1313                info.cxx = true;
1314             else if(!strcmpi(extension, "m"))
1315                info.m = true;
1316             else if(!strcmpi(extension, "mm"))
1317                info.mm = true;
1318             namesInfo[moduleName] = info;
1319          }
1320       }
1321       else if(files)
1322       {
1323          for(child : files)
1324          {
1325             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1326                child.GenMakefileGetNameCollisionInfo(namesInfo, prjConfig);
1327          }
1328       }
1329    }
1330    
1331    int GenMakefilePrintNode(File f, Project project, GenMakefilePrintTypes printType,
1332       Map<String, NameCollisionInfo> namesInfo, Array<String> items,
1333       ProjectConfig prjConfig, bool * containsCXX)
1334    {
1335       int count = 0;
1336       if(type == file)
1337       {
1338          char s[2048];
1339          TwoStrings ts = GetPlatformSpecificFu(prjConfig);
1340          char moduleName[MAX_FILENAME];
1341          char extension[MAX_EXTENSION];
1342          GetExtension(name, extension);
1343          if(printType == resources)
1344          {
1345             bool useRes;
1346             char tempPath[MAX_LOCATION];
1347             char modulePath[MAX_LOCATION];
1348
1349             tempPath[0] = '\0';
1350             if(eString_PathInsideOfMore(path, project.resNode.path, tempPath))
1351             {
1352                useRes = true;
1353                PathCatSlash(tempPath, name);
1354             }
1355             else
1356             {
1357                useRes = false;
1358                strcpy(tempPath, path);
1359                PathCatSlash(tempPath, name);
1360             }
1361             ReplaceSpaces(modulePath, tempPath);
1362             sprintf(s, "%s%s%s%s", ts.a, useRes ? "$(RES)" : "", modulePath, ts.b);
1363             items.Add(CopyString(s));
1364          }
1365          else if(printType == sources)
1366          {
1367             if(!strcmpi(extension, "s") || !strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1368                   !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1369                   !strcmpi(extension, "m") || !strcmpi(extension, "mm"))
1370             {
1371                char modulePath[MAX_LOCATION];
1372
1373                ReplaceSpaces(modulePath, path);
1374                ReplaceSpaces(moduleName, name);
1375                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
1376                items.Add(CopyString(s));
1377             }
1378          }
1379          else if(printType == eCsources)
1380          {
1381             if(!strcmpi(extension, "ec"))
1382             {
1383                char modulePath[MAX_LOCATION];
1384
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(!strcmpi(extension, "s") || !strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1393                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1394                !strcmpi(extension, "m") || !strcmpi(extension, "mm"))
1395          {
1396             if(printType == objects)
1397             {
1398                bool collision;
1399                NameCollisionInfo info;
1400                count++;
1401                ReplaceSpaces(moduleName, name);
1402                StripExtension(moduleName);
1403                info = namesInfo[moduleName];
1404                collision = info ? info.IsExtensionColliding(extension) : false;
1405                sprintf(s, "%s$(OBJ)%s%s%s.o%s", ts.a, moduleName, collision ? "." : "", collision ? extension : "", ts.b);
1406                items.Add(CopyString(s));
1407                if(containsCXX && (!strcmpi(extension, "cpp") || !strcmpi(extension, "cc") || !strcmpi(extension, "cxx")))
1408                   *containsCXX = true;
1409             }
1410          }
1411          delete ts;
1412       }
1413       else if(files)
1414       {
1415          for(child : files)
1416          {
1417             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1418                count += child.GenMakefilePrintNode(f, project, printType, namesInfo, items, prjConfig, containsCXX);
1419          }
1420       }
1421       return count;
1422    }
1423
1424    void GenMakefilePrintSymbolRules(File f, Project project,
1425          ProjectConfig prjConfig, //Map<Platform, bool> parentExcludedPlatforms,
1426          Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
1427    {
1428       int ifCount = 0;
1429       Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
1430       //ProjectNode child;
1431       //char objDir[MAX_LOCATION];
1432       //ReplaceSpaces(objDir, config.objDir.dir);
1433
1434       //eSystem_Log("Printing Symbol Rules\n");
1435       if(type == file)
1436       {
1437          char extension[MAX_EXTENSION];
1438          char modulePath[MAX_LOCATION];
1439          char moduleName[MAX_FILENAME];
1440
1441          GetExtension(name, extension);
1442          if(!strcmpi(extension, "ec"))
1443          {
1444             DualPipe dep;
1445             char command[2048];
1446
1447             ReplaceSpaces(moduleName, name);
1448             StripExtension(moduleName);
1449
1450             ReplaceSpaces(modulePath, path);
1451             if(modulePath[0]) strcat(modulePath, SEPS);
1452
1453             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
1454 #if 0
1455             // *** Dependency command ***
1456             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s", moduleName,
1457                modulePath, moduleName, extension);
1458
1459             // System Includes (from global settings)
1460             for(item : compiler.dirs[Includes])
1461             {
1462                strcat(command, " -isystem ");
1463                if(strchr(item, ' '))
1464                {
1465                   strcat(command, "\"");
1466                   strcat(command, item);
1467                   strcat(command, "\"");
1468                }
1469                else
1470                   strcat(command, item);
1471             }
1472
1473             for(item : project.includeDirs)
1474             {
1475                strcat(command, " -I");
1476                if(strchr(item, ' '))
1477                {
1478                   strcat(command, "\"");
1479                   strcat(command, item);
1480                   strcat(command, "\"");
1481                }
1482                else
1483                   strcat(command, item);
1484             }
1485             for(item : project.preprocessorDefs)
1486             {
1487                strcat(command, " -D");
1488                strcat(command, item);
1489             }
1490
1491             // Execute it
1492             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1493             {
1494                char line[1024];
1495                bool firstLine = true;
1496                bool result = true;
1497
1498                // To do some time: auto save external dependencies?
1499                while(!dep.Eof())
1500                {
1501                   if(dep.GetLine(line, sizeof(line)-1))
1502                   {
1503                      if(firstLine)
1504                      {
1505                         char * colon = strstr(line, ":");
1506                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1507                         {
1508                            result = false;
1509                            break;
1510                         }
1511                         firstLine = false;
1512                      }
1513                      f.Puts(line);
1514                      f.Puts("\n");
1515                   }
1516                   if(!result) break;
1517                }
1518                delete dep;
1519
1520                // If we failed to generate dependencies...
1521                if(!result)
1522                {
1523 #endif
1524                   f.Printf("$(OBJ)%s.sym: %s%s.%s\n",
1525                      moduleName, modulePath, moduleName, extension);
1526
1527                   f.Puts("\t$(ECP)");
1528
1529                   f.Puts(" $(CFLAGS)");
1530                   f.Puts(" $(CECFLAGS)"); // tocheck: what of this? should this stuff be per-file customized?
1531
1532                   GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
1533                   GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
1534
1535                   f.Printf(" -c %s%s.%s -o $(OBJ)%s.sym\n",
1536                      modulePath, moduleName, extension, moduleName);
1537                   if(ifCount) f.Puts("endif\n");
1538                   f.Puts("\n");
1539 #if 0
1540                }
1541             }
1542 #endif
1543          }
1544       }
1545       if(files)
1546       {
1547          bool needed = false;
1548          if(ContainsFilesWithExtension("ec"))
1549          {
1550             for(child : files)
1551             {
1552                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1553                {
1554                   needed = true;
1555                   break;
1556                }
1557             }
1558          }
1559          if(needed)
1560          {
1561             for(child : files)
1562             {
1563                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1564                   child.GenMakefilePrintSymbolRules(f, project, prjConfig, /*excludedPlatforms,*/
1565                         nodeCFlagsMapping, nodeECFlagsMapping);
1566             }
1567          }
1568       }
1569       delete platforms;
1570    }
1571
1572    void GenMakefilePrintPrepecsRules(File f, Project project,
1573          ProjectConfig prjConfig, /*Map<Platform, bool> parentExcludedPlatforms,*/
1574          Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
1575    {
1576       int ifCount = 0;
1577       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1578       Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
1579       //ProjectNode child;
1580       //char objDir[MAX_LOCATION];
1581       //ReplaceSpaces(objDir, config.objDir.dir);
1582
1583       //eSystem_Log("Printing Symbol Rules\n");
1584       if(type == file)
1585       {
1586          char extension[MAX_EXTENSION];
1587          char modulePath[MAX_LOCATION];
1588          char moduleName[MAX_FILENAME];
1589
1590          GetExtension(name, extension);
1591          if(!strcmpi(extension, "ec"))
1592          {
1593             DualPipe dep;
1594             char command[2048];
1595
1596             ReplaceSpaces(moduleName, name);
1597             StripExtension(moduleName);
1598
1599             ReplaceSpaces(modulePath, path);
1600             if(modulePath[0]) strcat(modulePath, SEPS);
1601
1602             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
1603             f.Printf("$(OBJ)%s$(EC): %s%s.%s\n",
1604                moduleName, modulePath, moduleName, extension);
1605             //$(CPP) -x c -E ../extras/gui/controls/DirectoriesBox.ec -o $(OBJ)DirectoriesBox$(EC)
1606             /*f.Printf("\t$(CPP) %s%s.%s %s$(S)\n\n",
1607                modulePath, moduleName, extension, moduleName);*/
1608
1609             f.Puts("\t$(CPP)");
1610
1611             f.Puts(" $(CFLAGS)");
1612             //f.Puts(" $(CECFLAGS)");
1613             //GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
1614             GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
1615
1616             f.Printf(" -x c -E %s%s.%s -o $(OBJ)%s$(EC)\n",
1617                modulePath, moduleName, extension, moduleName);
1618             if(ifCount) f.Puts("endif\n");
1619             f.Puts("\n");
1620          }
1621       }
1622       if(files)
1623       {
1624          bool needed = false;
1625          if(ContainsFilesWithExtension("ec"))
1626          {
1627             for(child : files)
1628             {
1629                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1630                {
1631                   needed = true;
1632                   break;
1633                }
1634             }
1635          }
1636          if(needed)
1637          {
1638             for(child : files)
1639             {
1640                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1641                   child.GenMakefilePrintPrepecsRules(f, project, prjConfig, /*excludedPlatforms,*/
1642                         nodeCFlagsMapping, nodeECFlagsMapping);
1643             }
1644          }
1645       }
1646       delete platforms;
1647    }
1648
1649    void GenMakefilePrintCObjectRules(File f, Project project,
1650       ProjectConfig prjConfig, /*Map<Platform, bool> parentExcludedPlatforms,*/
1651       Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
1652    {
1653       int ifCount = 0;
1654       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1655       Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
1656       //ProjectNode child;
1657       //char objDir[MAX_LOCATION];
1658       //ReplaceSpaces(objDir, config.objDir.dir);
1659       //eSystem_Log("Printing C Object Rules\n");
1660       if(type == file)
1661       {
1662          char extension[MAX_EXTENSION];
1663          char modulePath[MAX_LOCATION];
1664          char moduleName[MAX_FILENAME];
1665
1666          GetExtension(name, extension);
1667          if(!strcmpi(extension, "ec"))
1668          {
1669             DualPipe dep;
1670             char command[2048];
1671
1672             ReplaceSpaces(moduleName, name);
1673             StripExtension(moduleName);
1674
1675             ReplaceSpaces(modulePath, path);
1676             if(modulePath[0]) strcat(modulePath, SEPS);
1677
1678             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
1679 #if 0
1680             // *** Dependency command ***
1681             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s",
1682                moduleName, modulePath, moduleName, extension);
1683
1684             // System Includes (from global settings)
1685             for(item : compiler.dirs[Includes])
1686             {
1687                strcat(command, " -isystem ");
1688                if(strchr(item, ' '))
1689                {
1690                   strcat(command, "\"");
1691                   strcat(command, item);
1692                   strcat(command, "\"");
1693                }
1694                else
1695                   strcat(command, item);
1696             }
1697
1698             for(item : config.includeDirs)
1699             {
1700                strcat(command, " -I");
1701                if(strchr(item, ' '))
1702                {
1703                   strcat(command, "\"");
1704                   strcat(command, item);
1705                   strcat(command, "\"");
1706                }
1707                else
1708                   strcat(command, item);
1709             }
1710             for(item : config.preprocessorDefs)
1711             {
1712                strcat(command, " -D");
1713                strcat(command, item);
1714             }
1715
1716             // Execute it
1717             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1718             {
1719                char line[1024];
1720                bool result = true;
1721                bool firstLine = true;
1722
1723                // To do some time: auto save external dependencies?
1724                while(!dep.Eof())
1725                {
1726                   if(dep.GetLine(line, sizeof(line)-1))
1727                   {
1728                      if(firstLine)
1729                      {
1730                         char * colon = strstr(line, ":");
1731                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1732                         {
1733                            result = false;
1734                            break;
1735                         }
1736                         firstLine = false;
1737                      }
1738                      f.Puts(line);
1739                      f.Puts("\n");
1740                   }
1741                   if(!result) break;
1742                }
1743                delete dep;
1744
1745                // If we failed to generate dependencies...
1746                if(!result)
1747                {
1748                   /* COMMENTED OUT FOR NOW
1749                   f.Printf("$(OBJ)%s.c: %s%s.%s $(Symbols)\n",
1750                      moduleName, modulePath, moduleName, extension);
1751                   */
1752 #endif
1753                   f.Printf("$(OBJ)%s.c: %s%s.%s $(OBJ)%s.sym | $(SYMBOLS)\n",
1754                      moduleName, modulePath, moduleName, extension, moduleName);
1755 #if 0
1756                }
1757             }
1758 #endif
1759          /*
1760             f.Printf("\t$(ECC) %s%s.%s $(OBJ)%s.c\n\n",
1761                modulePath, moduleName, extension, moduleName);
1762          */
1763
1764             f.Puts("\t$(ECC)");
1765
1766             f.Puts(" $(CFLAGS)");
1767             f.Puts(" $(CECFLAGS)"); // what of this? should this stuff be per-file customized?
1768             GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
1769             GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
1770             f.Puts(" $(FVISIBILITY)");
1771
1772             f.Printf(" -c %s%s.%s -o $(OBJ)%s.c -symbols $(OBJ)\n",
1773                modulePath, moduleName, extension, moduleName);
1774             if(ifCount) f.Puts("endif\n");
1775             f.Puts("\n");
1776          }
1777       }
1778       if(files)
1779       {
1780          bool needed = false;
1781          if(ContainsFilesWithExtension("ec"))
1782          {
1783             for(child : files)
1784             {
1785                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1786                {
1787                   needed = true;
1788                   break;
1789                }
1790             }
1791          }
1792          if(needed)
1793          {
1794             for(child : files)
1795             {
1796                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1797                   child.GenMakefilePrintCObjectRules(f, project, prjConfig, /*excludedPlatforms,*/
1798                         nodeCFlagsMapping, nodeECFlagsMapping);
1799             }
1800          }
1801       }
1802       delete platforms;
1803    }
1804
1805    void GenMakefilePrintObjectRules(File f, Project project,
1806       Map<String, NameCollisionInfo> namesInfo,
1807       ProjectConfig prjConfig,
1808       //Map<Platform, bool> parentExcludedPlatforms,
1809       Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
1810    {
1811       int ifCount = 0;
1812       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1813       Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
1814       //ProjectNode child;
1815       //char objDir[MAX_LOCATION];
1816       //ReplaceSpaces(objDir, config.objDir.dir);
1817       //eSystem_Log("Printing Object Rules\n");
1818       if(type == file)
1819       {
1820          bool collision;
1821          char extension[MAX_EXTENSION];
1822          char modulePath[MAX_LOCATION];
1823          char moduleName[MAX_FILENAME];
1824
1825          GetExtension(name, extension);
1826          /*if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1827                !strcmpi(extension, "ec") || !strcmpi(extension, "cc") ||
1828                !strcmpi(extension, "cxx"))*/
1829          if(!strcmpi(extension, "s") || !strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1830                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1831                !strcmpi(extension, "m") || !strcmpi(extension, "mm") || !strcmpi(extension, "ec"))
1832          {
1833             DualPipe dep;
1834             char command[2048];
1835             NameCollisionInfo info;
1836
1837             ReplaceSpaces(moduleName, name);
1838             StripExtension(moduleName);
1839
1840             info = namesInfo[moduleName];
1841             collision = info ? info.IsExtensionColliding(extension) : false;
1842             
1843             ReplaceSpaces(modulePath, path);
1844             if(modulePath[0]) strcat(modulePath, SEPS);
1845
1846             // *** Dependency command ***
1847             if(!strcmpi(extension, "ec"))
1848                sprintf(command, "%s -MT $(OBJ)%s.o -MM $(OBJ)%s.c", "$(CPP)", moduleName, moduleName);
1849             else
1850                sprintf(command, "%s -MT $(OBJ)%s.o -MM %s%s.%s", (!strcmpi(extension, "cc") || !strcmpi(extension, "cxx") || !strcmpi(extension, "cpp")) ? "$(CXX)" : "$(CC)",
1851                   moduleName, modulePath, moduleName, extension);
1852
1853             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
1854             /*
1855 #if 0
1856             if(!strcmpi(extension, "ec"))
1857             {
1858                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1859             }
1860             else
1861             {
1862                // System Includes (from global settings)
1863                for(item : compiler.dirs[includes])
1864                {
1865                   strcat(command, " -isystem ");
1866                   if(strchr(item, ' '))
1867                   {
1868                      strcat(command, "\"");
1869                      strcat(command, item);
1870                      strcat(command, "\"");
1871                   }
1872                   else
1873                      strcat(command, item);
1874                }
1875
1876                for(item : config.includeDirs)
1877                {
1878                   strcat(command, " -I");
1879                   if(strchr(item, ' '))
1880                   {
1881                      strcat(command, "\"");
1882                      strcat(command, item);
1883                      strcat(command, "\"");
1884                   }
1885                   else
1886                      strcat(command, item);
1887                }
1888                for(item : config.preprocessorDefs)
1889                {
1890                   strcat(command, " -D");
1891                   strcat(command, item);
1892                }
1893
1894                // Execute it
1895                if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1896                {
1897                   char line[1024];
1898                   bool firstLine = true;
1899                   bool result = true;
1900
1901                   // To do some time: auto save external dependencies?
1902
1903                   while(!dep.Eof())
1904                   {
1905                      if(dep.GetLine(line, sizeof(line)-1))
1906                      {
1907                         if(firstLine)
1908                         {
1909                            char * colon = strstr(line, ":");
1910                            if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1911                            {
1912                               result = false;
1913                               break;
1914                            }
1915                            firstLine = false;
1916                         }
1917                         f.Puts(line);
1918                         f.Puts("\n");
1919                      }
1920                      if(!result) break;
1921                   }
1922                   delete dep;
1923
1924                   // If we failed to generate dependencies...
1925                   if(!result)
1926                   {
1927
1928                   }
1929                }
1930             }
1931 #endif
1932          */
1933             if(!strcmpi(extension, "ec"))
1934                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1935             else
1936                f.Printf("$(OBJ)%s%s%s.o: %s%s.%s\n", moduleName, 
1937                      collision ? "." : "", collision ? extension : "", modulePath, moduleName, extension);
1938             f.Printf("\t$(%s)", (!strcmpi(extension, "cc") || !strcmpi(extension, "cpp") || !strcmpi(extension, "cxx")) ? "CXX" : "CC");
1939
1940             f.Puts(" $(CFLAGS)");
1941             GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
1942
1943             if(!strcmpi(extension, "ec"))
1944                f.Printf(" $(FVISIBILITY) -c $(OBJ)%s.c -o $(OBJ)%s.o\n", moduleName, moduleName);
1945             else
1946                f.Printf(" -c %s%s.%s -o $(OBJ)%s%s%s.o\n",
1947                      modulePath, moduleName, !strcmpi(extension, "ec") ? "c" : extension, moduleName,
1948                      collision ? "." : "", collision ? extension : "");
1949
1950             if(ifCount) f.Puts("endif\n");
1951             f.Puts("\n");
1952          }
1953       }
1954       if(files)
1955       {
1956          bool needed = false;
1957          for(child : files)
1958          {
1959             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1960             {
1961                needed = true;
1962                break;
1963             }
1964          }
1965          if(needed)
1966          {
1967             for(child : files)
1968             {
1969                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1970                   child.GenMakefilePrintObjectRules(f, project, namesInfo, prjConfig, /*excludedPlatforms,*/
1971                         nodeCFlagsMapping, nodeECFlagsMapping);
1972             }
1973          }
1974       }
1975       delete platforms;
1976    }
1977
1978    void GenMakefileAddResources(File f, String resourcesPath, ProjectConfig prjConfig)
1979    {
1980       int count = 0;
1981       if(files)
1982       {
1983          int c;
1984          bool prev = false;
1985          //Iterator<ProjectNode> i { files };
1986          //Iterator<ProjectNode> prev { files };
1987          //for(child : files)
1988          //while(i.Next())
1989          for(c = 0; c < files.count; c++)
1990          {
1991             ProjectNode child = files[c];
1992             TwoStrings ts = child.GetPlatformSpecificFu(prjConfig);
1993             if(count > 0 && ts)
1994                prev = true;
1995             if(child.type == file && !child.GetIsExcluded(prjConfig) && !(count > 0 && ts))
1996             {
1997                bool useRes;
1998                char tempPath[MAX_LOCATION];
1999                char resPath[MAX_LOCATION];
2000
2001                char * quotes;
2002
2003                // $(EAR) aw%s --- /*quiet ? "q" : */""
2004                if(count == 0)
2005                   f.Printf("\t%s$(EAR) aw$(EARFLAGS) $(TARGET)", ts.a);
2006
2007                tempPath[0] = '\0';
2008                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
2009                {
2010                   useRes = true;
2011                   PathCatSlash(tempPath, child.name);
2012                }
2013                else
2014                {
2015                   useRes = false;
2016                   strcpy(tempPath, child.path);
2017                   PathCatSlash(tempPath, child.name);
2018                }
2019                ReplaceSpaces(resPath, tempPath);
2020                if(strchr(tempPath, ' '))
2021                   quotes = "\"";
2022                else
2023                   quotes = "";
2024                f.Printf(" %s%s%s%s", quotes, useRes ? "$(RES)" : "", tempPath, quotes);
2025                count++;
2026             }
2027             if(count == 10 || (count > 0 && (ts || !child.next)))
2028             {
2029                char path[MAX_LOCATION] = "", temp[MAX_LOCATION];
2030                ProjectNode parent;
2031
2032                for(parent = this; parent.type == folder; parent = parent.parent)
2033                {
2034                   strcpy(temp, path);
2035                   strcpy(path, parent.name);
2036                   if(temp[0])
2037                   {
2038                      strcat(path, "/");
2039                      strcat(path, temp);
2040                   }
2041                }
2042                f.Printf(" \"%s\"%s\n", path, ts.b);
2043                count = 0;
2044                if(prev)
2045                {
2046                   c--;
2047                   prev = false;
2048                }
2049             }
2050             delete ts;
2051          }
2052          for(child : files)
2053          {
2054             if(child.type == folder)
2055                child.GenMakefileAddResources(f, resourcesPath, prjConfig);
2056          }
2057       }
2058    }
2059
2060    void GenMakeCollectAssignNodeFlags(ProjectConfig prjConfig, bool prjWithEcFiles,
2061          Map<String, int> cflagsVariations, Map<intptr, int> nodeCFlagsMapping,
2062          Map<String, int> ecflagsVariations, Map<intptr, int> nodeECFlagsMapping,
2063          Map<Platform, ProjectOptions> parentByPlatformOptions)
2064    {
2065       Map<Platform, ProjectOptions> byPlatformOptions = parentByPlatformOptions;
2066       if(type == file || type == folder || type == project)
2067       {
2068          bool hasPerNodeOptions = type == project;
2069          if(!hasPerNodeOptions)
2070          {
2071             if(options && !options.isEmpty)
2072                hasPerNodeOptions = true;
2073             else if(configurations)
2074             {
2075                for(c : configurations)
2076                {
2077                   if(c.options && !c.options.isEmpty)
2078                   {
2079                      hasPerNodeOptions = true;
2080                      break;
2081                   }
2082                   if(c.platforms)
2083                   {
2084                      for(p : c.platforms)
2085                      {
2086                         if(p.options && !p.options.isEmpty)
2087                         {
2088                            hasPerNodeOptions = true;
2089                            break;
2090                         }
2091                      }
2092                      if(hasPerNodeOptions)
2093                         break;
2094                   }
2095                }
2096             }
2097             if(!hasPerNodeOptions && platforms)
2098             {
2099                for(p : platforms)
2100                {
2101                   if(p.options && !p.options.isEmpty)
2102                   {
2103                      hasPerNodeOptions = true;
2104                      break;
2105                   }
2106                }
2107             }
2108
2109          }
2110          if(hasPerNodeOptions)
2111          {
2112             bool isEqual = false, isGreater = false;
2113             ComplexComparison complexCmp;
2114             DynamicString s;
2115             Map<Platform, ProjectOptions> additionsByPlatformOptions { };
2116             ProjectOptions platformsCommonOptions;
2117             ProjectOptions byFileConfigPlatformProjectOptions;
2118
2119             DynamicString cflags { };
2120             DynamicString ecflags { };
2121
2122             Platform platform;
2123
2124             byPlatformOptions = { };
2125
2126             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
2127             {
2128                byFileConfigPlatformProjectOptions =
2129                      BlendFileConfigPlatformProjectOptions(this, prjConfig, platform);
2130                byPlatformOptions[platform] = byFileConfigPlatformProjectOptions;
2131             }
2132
2133             CollectPlatformsCommonOptions(byPlatformOptions, &platformsCommonOptions);
2134
2135             byPlatformOptions[unknown] = platformsCommonOptions;
2136
2137             if(parentByPlatformOptions)
2138             {
2139                complexCmp = PlatformsOptionsGreaterEqual(byPlatformOptions,
2140                      parentByPlatformOptions, additionsByPlatformOptions);
2141                isGreater = complexCmp == greater;
2142                isEqual = complexCmp == equal;
2143             }
2144
2145             if(!isEqual)
2146             {
2147                for(platform = (Platform)1; platform < Platform::enumSize; platform++)
2148                {
2149                   byFileConfigPlatformProjectOptions = isGreater ? additionsByPlatformOptions[platform] : byPlatformOptions[platform];
2150                   s = { };
2151                   GenCFlagsFromProjectOptions(byFileConfigPlatformProjectOptions, prjWithEcFiles, false, isGreater, s);
2152                   if(s.count > 1)
2153                      cflags.concatf(" \\\n\t $(if $(%s),%s,)", PlatformToMakefileTargetVariable(platform), (String)s);
2154                   delete s;
2155                   s = { };
2156                   GenECFlagsFromProjectOptions(byFileConfigPlatformProjectOptions, prjWithEcFiles, s);
2157                   if(s.count > 1)
2158                      ecflags.concatf(" \\\n\t $(if $(%s),%s,)", PlatformToMakefileTargetVariable(platform), (String)s);
2159                   delete s;
2160                }
2161
2162                platformsCommonOptions = isGreater ? additionsByPlatformOptions[unknown] : byPlatformOptions[unknown];
2163                s = { };
2164                GenCFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, true, isGreater, s);
2165                if(s.count > 1)
2166                {
2167                   if(!isGreater) cflags.concat(" \\\n\t");
2168                   cflags.concat(s);
2169                }
2170                delete s;
2171                s = { };
2172                GenECFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, s);
2173                if(s.count > 1)
2174                {
2175                   ecflags.concat(" \\\n\t");
2176                   ecflags.concat(s);
2177                }
2178                delete s;
2179
2180                if(isGreater)
2181                {
2182                   cflags.concat(" \\\n\t");
2183                   DynStringPrintNodeFlagsVariable(parent, nodeCFlagsMapping, "PRJ_CFLAGS", cflags);
2184                }
2185             }
2186
2187             additionsByPlatformOptions.Free();
2188             delete additionsByPlatformOptions;
2189
2190             // output
2191             {
2192                if(isEqual)
2193                {
2194                   nodeCFlagsMapping[(intptr)this] = nodeCFlagsMapping[(intptr)parent];
2195                   nodeECFlagsMapping[(intptr)this] = nodeECFlagsMapping[(intptr)parent];
2196                }
2197                else
2198                {
2199                   String s;
2200                   int variationNum;
2201
2202                   variationNum = 1;
2203                   if((s = cflags) && s[0] && !(variationNum = cflagsVariations[s]))
2204                      cflagsVariations[s] = variationNum = cflagsVariations.count;
2205                   nodeCFlagsMapping[(intptr)this] = variationNum;
2206
2207                   variationNum = 1;
2208                   if((s = ecflags) && s[0] && !(variationNum = ecflagsVariations[s]))
2209                      ecflagsVariations[s] = variationNum = ecflagsVariations.count;
2210                   nodeECFlagsMapping[(intptr)this] = variationNum;
2211                }
2212             }
2213
2214             delete cflags;
2215             delete ecflags;
2216          }
2217          else
2218          {
2219             // output
2220             {
2221                nodeCFlagsMapping[(intptr)this] = nodeCFlagsMapping[(intptr)parent];
2222                nodeECFlagsMapping[(intptr)this] = nodeECFlagsMapping[(intptr)parent];
2223             }
2224          }
2225
2226       }
2227       if(files)
2228       {
2229          for(child : files)
2230          {
2231             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2232                child.GenMakeCollectAssignNodeFlags(prjConfig, prjWithEcFiles,
2233                      cflagsVariations, nodeCFlagsMapping, ecflagsVariations, nodeECFlagsMapping,
2234                      byPlatformOptions);
2235          }
2236       }
2237
2238       if(byPlatformOptions != parentByPlatformOptions)
2239       {
2240          byPlatformOptions.Free();
2241          delete byPlatformOptions;
2242       }
2243    }
2244
2245    Array<Platform> GetPlatformsArrayFromExclusionInfo(ProjectConfig prjConfig)
2246    {
2247       Array<Platform> platforms { };
2248       Map<Platform, SetBool> exclusionInfo { };
2249       CollectExclusionInfo(exclusionInfo, prjConfig);
2250
2251       if(exclusionInfo[unknown] == true)
2252       {
2253          if(exclusionInfo.count > 1)
2254             for(p : exclusionInfo; p == false)
2255                platforms.Add(&p);
2256       }
2257       else
2258       {
2259          bool onlyOnknown = true;
2260          for(p : exclusionInfo)
2261             if(&p != unknown && p == true)
2262             {
2263                onlyOnknown = false;
2264                break;
2265             }
2266          if(onlyOnknown)
2267             platforms.Add(unknown);
2268          else
2269          {
2270             Platform p;
2271             for(p = unknown + 1; p < Platform::enumSize; p++)
2272                if(exclusionInfo[p] != true)
2273                   platforms.Add(p);
2274          }
2275       }
2276       delete exclusionInfo;
2277       return platforms;
2278    }
2279
2280    void GetTargets(ProjectConfig prjConfig, Map<String, NameCollisionInfo> namesInfo, char * objDir, DynamicString output)
2281    {
2282       if(type == file)
2283       {
2284          bool headerAltFailed = false;
2285          bool collision;
2286          char extension[MAX_EXTENSION];
2287          char moduleName[MAX_FILENAME];
2288          NameCollisionInfo info;
2289          Project prj = property::project;
2290          Map<String, String> headerToSource { [ { "eh", "ec" }, { "h", "c" }, { "hh", "cc" }, { "hpp", "cpp" }, { "hxx", "cxx" } ] };
2291
2292          GetExtension(name, extension);
2293          ReplaceSpaces(moduleName, name);
2294          StripExtension(moduleName);
2295          info = namesInfo[moduleName];
2296          collision = info ? info.IsExtensionColliding(extension) : false;
2297
2298          for(h2s : headerToSource)
2299          {
2300             if(!strcmpi(extension, &h2s))
2301             {
2302                char filePath[MAX_LOCATION];
2303                GetFullFilePath(filePath);
2304                OutputLog($"No compilation required for header file "); OutputLog(filePath); OutputLog("\n");
2305                ChangeExtension(moduleName, h2s, moduleName);
2306                if(prj.topNode.Find(moduleName, false))
2307                {
2308                   strcpy(extension, h2s);
2309                   collision = info ? info.IsExtensionColliding(extension) : false;
2310                   ChangeExtension(filePath, h2s, filePath);
2311                   OutputLog($"Compiling source file "); OutputLog(filePath); OutputLog($" instead\n");
2312                   StripExtension(moduleName);
2313                }
2314                else
2315                {
2316                   headerAltFailed = true;
2317                   OutputLog($"Unable to locate source file "); OutputLog(moduleName); OutputLog($" to compile instead of "); OutputLog(filePath); OutputLog($"\n");
2318                   StripExtension(moduleName);
2319                }
2320                break;
2321             }
2322          }
2323
2324          if(!headerAltFailed)
2325          {
2326             output.concat(" \"");
2327             output.concat(objDir); //.concat(" $(OBJ)");
2328             output.concat("/");
2329
2330             if(collision)
2331             {
2332                strcat(moduleName, ".");
2333                strcat(moduleName, extension);
2334             }
2335             strcat(moduleName, ".o");
2336             output.concat(moduleName);
2337             output.concat("\"");
2338          }
2339       }
2340       else if(files)
2341       {
2342          for(child : files)
2343          {
2344             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2345                child.GetTargets(prjConfig, namesInfo, objDir, output);
2346          }
2347       }
2348    }
2349
2350    void DeleteIntermediateFiles(CompilerConfig compiler, ProjectConfig prjConfig, int bitDepth, Map<String, NameCollisionInfo> namesInfo, bool onlyCObject)
2351    {
2352       if(type == file)
2353       {
2354          bool collision;
2355          char extension[MAX_EXTENSION];
2356          char fileName[MAX_FILENAME];
2357          char moduleName[MAX_FILENAME];
2358          NameCollisionInfo info;
2359          Project prj = property::project;
2360          DirExpression objDir = prj.GetObjDir(compiler, prjConfig, bitDepth);
2361
2362          GetExtension(name, extension);
2363          ReplaceSpaces(moduleName, name);
2364          StripExtension(moduleName);
2365          info = namesInfo[moduleName];
2366          collision = info ? info.IsExtensionColliding(extension) : false;
2367
2368          strcpy(fileName, prj.topNode.path);
2369          PathCatSlash(fileName, objDir.dir);
2370          PathCatSlash(fileName, name);
2371
2372          if(!onlyCObject && !strcmp(extension, "ec"))
2373          {
2374             ChangeExtension(fileName, "c", fileName);
2375             if(FileExists(fileName)) DeleteFile(fileName);
2376             ChangeExtension(fileName, "sym", fileName);
2377             if(FileExists(fileName)) DeleteFile(fileName);
2378             ChangeExtension(fileName, "imp", fileName);
2379             if(FileExists(fileName)) DeleteFile(fileName);
2380             ChangeExtension(fileName, "bowl", fileName);
2381             if(FileExists(fileName)) DeleteFile(fileName);
2382             ChangeExtension(fileName, "ec", fileName);
2383          }
2384
2385          if(collision)
2386             strcat(fileName, ".o");
2387          else
2388             ChangeExtension(fileName, "o", fileName);
2389          if(FileExists(fileName)) DeleteFile(fileName);
2390
2391          delete objDir;
2392       }
2393       else if(files)
2394       {
2395          for(child : files)
2396          {
2397             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2398                child.DeleteIntermediateFiles(compiler, prjConfig, bitDepth, namesInfo, onlyCObject);
2399          }
2400       }
2401    }
2402
2403    bool IsInNode(ProjectNode node)
2404    {
2405       bool result = false;
2406       ProjectNode n;
2407       for(n = this; n; n = n.parent)
2408       {
2409          if(n == node)
2410          {
2411             result = true;
2412             break;
2413          }
2414       }
2415       return result;
2416    }
2417 }
2418
2419 // the code in this function is closely matched to OptionsBox::Load
2420 // and accompanying derivations of OptionBox and their use of OptionSet,
2421 // OptionCheck, LoadOption and FinalizeLoading methods.
2422 // output changing modification should be mirrored in both implementations
2423 static ProjectOptions BlendFileConfigPlatformProjectOptions(ProjectNode node, ProjectConfig projectConfig, Platform platform)
2424 {
2425    ProjectOptions output { };
2426
2427    // legend: e Element
2428    //         o Option (of a ProjectOptions)
2429    //         n Node (ProjectNode)
2430    //         p Platform
2431    //         u Utility (GenericOptionTools)
2432
2433    int e;
2434    int o;
2435    int priority = 0;
2436    int includeDirsOption = OPTION(includeDirs);
2437    ProjectNode n;
2438    char * platformName = platform ? platform.OnGetString(0,0,0) : null;
2439
2440    Array<bool> optionConfigXplatformSet   { size = OPTION(postbuildCommands) };
2441    Array<bool> optionDone                 { size = OPTION(postbuildCommands) };
2442    Array<Array<String>> optionTempStrings { size = OPTION(postbuildCommands) };
2443
2444    GenericOptionTools<SetBool>              utilSetBool {
2445       bool OptionCheck(ProjectOptions options, int option) {
2446          return *(SetBool*)((byte *)options + option) == true;
2447       }
2448       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2449          if(options && (*(SetBool*)((byte *)options + option) == true))
2450             *(SetBool*)((byte *)output + option) = true;
2451       }
2452    };
2453    GenericOptionTools<String>               utilString {
2454       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2455          String * string = (String*)((byte *)output + option);
2456          if(*string) delete *string;
2457          if(options)
2458             *string = CopyString(*(String*)((byte *)options + option));
2459       }
2460    };
2461    StringArrayOptionTools                   utilStringArrays {
2462       mergeValues = true;
2463       caseSensitive = true;
2464       bool OptionCheck(ProjectOptions options, int option) {
2465          Array<String> strings = *(Array<String>*)((byte *)options + option);
2466          return strings && strings.count;
2467       }
2468       bool OptionSet(ProjectOptions options, int option) {
2469          Array<String> strings = *(Array<String>*)((byte *)options + option);
2470          if(mergeValues && !configReplaces)
2471             return strings && strings.count;
2472          else
2473             return strings != null;
2474       }
2475       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2476          if(mergeValues)
2477          {
2478             Array<String> strings = options ? *((Array<String>*)((byte *)options + option) : null;
2479             if(strings)
2480             {
2481                int order = 0;
2482                Array<String> tempStrings = optionTempStrings[option];
2483                if(!tempStrings)
2484                   optionTempStrings[option] = tempStrings = { };
2485                for(s : strings)
2486                {
2487                   bool found = false;
2488                   char priorityMark[10];
2489                   order++;
2490                   if(priority)
2491                      sprintf(priorityMark, "%06d\n", priority * 1000 + order);
2492                   for(i : tempStrings; !(caseSensitive ? strcmp : strcmpi)(i, s)) { found = true; break; }
2493                   if(!found) tempStrings.Add(priority ? PrintString(priorityMark, s) : CopyString(s));
2494                }
2495             }
2496          }
2497          else
2498          {
2499             Array<String> * newStrings = (Array<String>*)((byte *)options + option);
2500             Array<String> * strings = (Array<String>*)((byte *)output + option);
2501             if(*strings) { strings->Free(); delete *strings; }
2502             if(*newStrings && newStrings->count) { *strings = { }; strings->Copy((void*)*newStrings); }
2503          }
2504       }
2505       void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2506          if(mergeValues)
2507          {
2508             Array<String> tempStrings = optionTempStrings[option];
2509             Array<String> * strings = (Array<String>*)((byte *)output + option);
2510             if(*strings) { strings->Free(); delete *strings; }
2511             if(tempStrings && tempStrings.count) { *strings = { }; strings->Copy((void*)tempStrings); }
2512             delete tempStrings;
2513          }
2514       }
2515    };
2516    GenericOptionTools<WarningsOption>       utilWarningsOption {
2517       bool OptionCheck(ProjectOptions options, int option) {
2518          WarningsOption value = *(WarningsOption*)((byte *)options + option);
2519          return value && value != none;
2520       }
2521       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2522          WarningsOption value = options ? *(WarningsOption*)((byte *)options + option) : (WarningsOption)0;
2523          *(WarningsOption*)((byte *)output + option) = value;
2524       }
2525    };
2526    GenericOptionTools<OptimizationStrategy> utilOptimizationStrategy {
2527       bool OptionCheck(ProjectOptions options, int option) {
2528          OptimizationStrategy value = *(OptimizationStrategy*)((byte *)options + option);
2529          return value && value != none;
2530       }
2531       void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
2532          OptimizationStrategy value = options ? *(OptimizationStrategy*)((byte *)options + option) : (OptimizationStrategy)0;
2533          *(OptimizationStrategy*)((byte *)output + option) = value;
2534       }
2535    };
2536
2537    Map<int, GenericOptionTools> ot { };
2538
2539    // The following are compiler options
2540
2541    ot[OPTION(debug)] =                   utilSetBool;
2542    ot[OPTION(memoryGuard)] =             utilSetBool;
2543    ot[OPTION(profile)] =                 utilSetBool;
2544    ot[OPTION(noLineNumbers)] =           utilSetBool;
2545    ot[OPTION(strictNameSpaces)] =        utilSetBool;
2546    ot[OPTION(fastMath)] =                utilSetBool;
2547
2548    ot[OPTION(defaultNameSpace)] =        utilString;
2549
2550    ot[OPTION(preprocessorDefinitions)] = utilStringArrays;
2551    ot[OPTION(includeDirs)] =             utilStringArrays;
2552
2553    ot[OPTION(warnings)] =                utilWarningsOption;
2554
2555    ot[OPTION(optimization)] =            utilOptimizationStrategy;
2556
2557    for(n = node; n; n = n.parent)
2558    {
2559       ProjectConfig nodeConfig = null;
2560       if(n.parent)
2561          priority = (priority / 10 + 1) * 10;
2562       else
2563          priority = 9990;
2564       if(projectConfig && n.configurations)
2565       {
2566          for(c : n.configurations; !strcmpi(c.name, projectConfig.name))
2567          {
2568             if(platform && c.platforms)
2569             {
2570                for(p : c.platforms; !strcmpi(p.name, platformName))
2571                {
2572                   for(uu : ot)
2573                   {
2574                      GenericOptionTools u = uu;
2575                      o = &uu;
2576                      if(!optionDone[o] && p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
2577                      {
2578                         u.LoadOption(p.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
2579                         if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2580                         optionConfigXplatformSet[o] = true;
2581                      }
2582                   }
2583                   break;
2584                }
2585             }
2586             nodeConfig = c;
2587             break;
2588          }
2589       }
2590       for(uu : ot)
2591       {
2592          GenericOptionTools u = uu;
2593          o = &uu;
2594          if(!optionDone[o])
2595          {
2596             if(platform && n.platforms && (!optionConfigXplatformSet[o] || !u.configReplaces))
2597             {
2598                for(p : n.platforms; !strcmpi(p.name, platformName))
2599                {
2600                   if(p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
2601                   {
2602                      u.LoadOption(p.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2603                      if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2604                   }
2605                   break;
2606                }
2607             }
2608             if(!optionDone[o] && nodeConfig && nodeConfig.options &&
2609                   ((u.mergeValues && !u.configReplaces) ?
2610                         u.OptionCheck(nodeConfig.options, o) :
2611                         u.OptionSet(nodeConfig.options, o)))
2612             {
2613                u.LoadOption(nodeConfig.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
2614                if(!u.mergeValues || u.configReplaces) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2615             }
2616             if(!optionDone[o])
2617             {
2618                if(n.options && (u.mergeValues ? u.OptionCheck(n.options, o) : u.OptionSet(n.options, o)))
2619                {
2620                   u.LoadOption(n.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2621                   if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2622                }
2623                else if(!n.parent)
2624                {
2625                   u.LoadOption(null, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
2626                   if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
2627                }
2628             }
2629          }
2630       }
2631    }
2632    for(uu : ot)
2633    {
2634       GenericOptionTools u = uu;
2635       o = &uu;
2636       if(!optionDone[o])
2637          u.FinalizeLoading(o, optionTempStrings, output);
2638    }
2639
2640    delete optionConfigXplatformSet;
2641    delete optionDone;
2642    delete optionTempStrings;
2643
2644    delete utilSetBool;
2645    delete utilString;
2646    delete utilStringArrays;
2647    delete utilWarningsOption;
2648    delete utilOptimizationStrategy;
2649
2650    delete ot;
2651
2652    return output;
2653 }
2654
2655 static void CollectPlatformsCommonOptions(Map<Platform, ProjectOptions> byPlatformOptions, ProjectOptions * platformsCommonOptions)
2656 {
2657    char * s;
2658    int i;
2659    ProjectOptions first;
2660    ProjectOptions commonOptions;
2661
2662    Map<String, int> countIncludeDirs { };
2663    Map<String, int> countPreprocessorDefinitions { };
2664    Map<String, bool> commonIncludeDirs { };
2665    Map<String, bool> commonPreprocessorDefinitions { };
2666
2667    for(options : byPlatformOptions) { first = options; break; }
2668
2669    *platformsCommonOptions = commonOptions = first.Copy();
2670
2671    if(commonOptions.includeDirs)
2672       commonOptions.includeDirs.Free();
2673    if(commonOptions.preprocessorDefinitions)
2674       commonOptions.preprocessorDefinitions.Free();
2675
2676    for(options : byPlatformOptions)
2677    {
2678       if(options != first)
2679       {
2680          if(commonOptions.debug && options.debug != commonOptions.debug)
2681             commonOptions.debug = unset;
2682          if(commonOptions.memoryGuard && options.memoryGuard != commonOptions.memoryGuard)
2683             commonOptions.memoryGuard = unset;
2684          if(commonOptions.profile && options.profile != commonOptions.profile)
2685             commonOptions.profile = unset;
2686          if(commonOptions.noLineNumbers && options.noLineNumbers != commonOptions.noLineNumbers)
2687             commonOptions.noLineNumbers = unset;
2688          if(commonOptions.strictNameSpaces && options.strictNameSpaces != commonOptions.strictNameSpaces)
2689             commonOptions.strictNameSpaces = unset;
2690          if(commonOptions.fastMath && options.fastMath != commonOptions.fastMath)
2691             commonOptions.fastMath = unset;
2692
2693          if(commonOptions.warnings && options.warnings != commonOptions.warnings)
2694             commonOptions.warnings = unset;
2695          if(commonOptions.optimization && options.optimization != commonOptions.optimization)
2696             commonOptions.optimization = unset;
2697
2698          if(commonOptions.defaultNameSpace && strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
2699             delete commonOptions.defaultNameSpace;
2700       }
2701
2702       CountSameNonEmptyOrNullStrings(options.includeDirs, countIncludeDirs);
2703       CountSameNonEmptyOrNullStrings(options.preprocessorDefinitions, countPreprocessorDefinitions);
2704    }
2705
2706    GetPlatformsCommonStrings(countIncludeDirs, byPlatformOptions.count,
2707          commonIncludeDirs, commonOptions.includeDirs);
2708    GetPlatformsCommonStrings(countPreprocessorDefinitions, byPlatformOptions.count,
2709          commonPreprocessorDefinitions, commonOptions.preprocessorDefinitions);
2710
2711    for(options : byPlatformOptions)
2712    {
2713       if(options.debug && options.debug == commonOptions.debug)
2714          options.debug = unset;
2715       if(options.memoryGuard && options.memoryGuard == commonOptions.memoryGuard)
2716          options.memoryGuard = unset;
2717       if(options.profile && options.profile == commonOptions.profile)
2718          options.profile = unset;
2719       if(options.noLineNumbers && options.noLineNumbers == commonOptions.noLineNumbers)
2720          options.noLineNumbers = unset;
2721       if(options.strictNameSpaces && options.strictNameSpaces == commonOptions.strictNameSpaces)
2722          options.strictNameSpaces = unset;
2723       if(options.fastMath && options.fastMath == commonOptions.fastMath)
2724          options.fastMath = unset;
2725
2726       if(options.warnings && options.warnings == commonOptions.warnings)
2727          options.warnings = unset;
2728       if(options.optimization && options.optimization == commonOptions.optimization)
2729          options.optimization = unset;
2730
2731       if(options.defaultNameSpace && !strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
2732          delete options.defaultNameSpace;
2733
2734       RemovePlatformsCommonStrings(commonIncludeDirs, options.includeDirs);
2735       RemovePlatformsCommonStrings(commonPreprocessorDefinitions, options.preprocessorDefinitions);
2736    }
2737
2738    delete countIncludeDirs;
2739    delete countPreprocessorDefinitions;
2740    delete commonIncludeDirs;
2741    delete commonPreprocessorDefinitions;
2742 }
2743
2744 static ComplexComparison PlatformsOptionsGreaterEqual(Map<Platform, ProjectOptions> byPlatformOptions,
2745       Map<Platform, ProjectOptions> parentByPlatformOptions,
2746       Map<Platform, ProjectOptions> additionsByPlatformOptions)
2747 {
2748    ComplexComparison result = equal;
2749    ComplexComparison compare;
2750    Platform platform;
2751    for(platform = (Platform)0; platform < Platform::enumSize; platform++)
2752    {
2753       ProjectOptions additionalOptions;
2754       additionsByPlatformOptions[platform] = { };
2755       additionalOptions = additionsByPlatformOptions[platform];
2756       compare = ExtractPlatformsOptionsAdditions(byPlatformOptions[platform], parentByPlatformOptions[platform], additionalOptions);
2757       if(compare == greater && result == equal)
2758          result = greater;
2759       else if(compare == different)
2760       {
2761          result = different;
2762          break;
2763       }
2764    }
2765    return result;
2766 }
2767
2768 static ComplexComparison ExtractPlatformsOptionsAdditions(ProjectOptions options, ProjectOptions parentOptions, ProjectOptions additionalOptions)
2769 {
2770    ComplexComparison result = equal;
2771    if(options.debug != parentOptions.debug ||
2772          options.memoryGuard != parentOptions.memoryGuard ||
2773          options.profile != parentOptions.profile ||
2774          options.noLineNumbers != parentOptions.noLineNumbers ||
2775          options.strictNameSpaces != parentOptions.strictNameSpaces ||
2776          options.fastMath != parentOptions.fastMath ||
2777          options.warnings != parentOptions.warnings ||
2778          options.optimization != parentOptions.optimization ||
2779          (options.defaultNameSpace != parentOptions.defaultNameSpace &&
2780                strcmp(options.defaultNameSpace, parentOptions.defaultNameSpace)))
2781       result = different;
2782    else
2783    {
2784       if(!StringsAreSameOrMore(options.includeDirs, parentOptions.includeDirs, &additionalOptions.includeDirs) ||
2785             !StringsAreSameOrMore(options.preprocessorDefinitions, parentOptions.preprocessorDefinitions, &additionalOptions.preprocessorDefinitions))
2786          result = different;
2787       if((additionalOptions.includeDirs && additionalOptions.includeDirs.count) ||
2788             (additionalOptions.preprocessorDefinitions && additionalOptions.preprocessorDefinitions.count))
2789          result = greater;
2790    }
2791    return result;
2792 }
2793
2794 enum ComplexComparison { different/*, smaller*/, equal, greater };
2795
2796 static bool StringsAreSameOrMore(Array<String> strings, Array<String> originals, Array<String> * additions)
2797 {
2798    bool result = true;
2799    if((!strings || !strings.count) && originals && originals.count)
2800       result = false;
2801    else if(strings && strings.count && (!originals || !originals.count))
2802    {
2803       if(!*additions)
2804          *additions = { };
2805       for(s : strings)
2806          additions->Add(CopyString(s));
2807    }
2808    else if(strings && strings.count && originals && originals.count)
2809    {
2810       Map<String, String> map { };
2811       MapIterator<String, bool> mit { map = map };
2812       for(it : strings)
2813       {
2814          char * s = strstr(it, "\n");
2815          s = s ? s+1 : it;
2816          map[s] = it;
2817       }
2818       for(it : originals)
2819       {
2820          char * s = strstr(it, "\n");
2821          s = s ? s+1 : it;
2822          if(!mit.Index(s, false))
2823          {
2824             result = false;
2825             break;
2826          }
2827          else
2828             map[s] = null;
2829       }
2830       if(result)
2831       {
2832          if(!*additions)
2833             *additions = { };
2834          for(it : map)
2835          {
2836             if(it)
2837                additions->Add(CopyString(it));
2838          }
2839       }
2840       delete map;
2841    }
2842    return result;
2843 }
2844
2845 static void CountSameNonEmptyOrNullStrings(Array<String> strings, Map<String, int> counts)
2846 {
2847    if(strings)
2848    {
2849       for(it : strings)
2850       {
2851          char * s = it;
2852          if(s && s[0])
2853             counts[s]++;
2854       }
2855    }
2856 }
2857
2858 static void GetPlatformsCommonStrings(Map<String, int> counts, int goodCount, Map<String, bool> common, Array<String> strings)
2859 {
2860    for(it : counts)
2861    {
2862       int i = it;
2863       if(i == goodCount)
2864       {
2865          char * s = &it;
2866          strings.Add(CopyString(s));
2867          common[s] = true;
2868       }
2869    }
2870 }
2871
2872 static void RemovePlatformsCommonStrings(Map<String, bool> common, Array<String> strings)
2873 {
2874    if(strings)
2875    {
2876       Array<String> tmp { };
2877       MapIterator<String, bool> mit { map = common };
2878       for(it : strings)
2879       {
2880          char * s = it;
2881          if(!mit.Index(s, false))
2882             tmp.Add(CopyString(s));
2883       }
2884       strings.Free();
2885       if(tmp.count)
2886       {
2887          for(s : tmp)
2888             strings.Add(CopyString(s));
2889          tmp.Free();
2890       }
2891       delete tmp;
2892    }
2893 }
2894
2895 static void GenMakePrintNodeFlagsVariable(ProjectNode node, Map<intptr, int> nodeFlagsMapping, String variableName, File f)
2896 {
2897    int customFlags;
2898    customFlags = nodeFlagsMapping[(intptr)node];
2899    if(customFlags > 1)
2900       f.Printf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
2901    else
2902       f.Printf(" $(%s)", variableName);
2903 }
2904
2905 static void DynStringPrintNodeFlagsVariable(ProjectNode node, Map<intptr, int> nodeFlagsMapping, String variableName, DynamicString s)
2906 {
2907    int customFlags;
2908    customFlags = nodeFlagsMapping[(intptr)node];
2909    if(customFlags > 1)
2910       s.concatf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
2911    else
2912       s.concatf(" $(%s)", variableName);
2913 }
2914
2915 static void GenCFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, bool commonOptions, bool isGreater, DynamicString s)
2916 {
2917    if(!isGreater)
2918    {
2919       //if(gccCompiler)
2920       {
2921          if(options.optimization == speed || options.optimization == size ||
2922                options.fastMath == true || options.debug == true)
2923          {
2924             if(options.debug != true)
2925             {
2926                s.concat(" $(if $(DEBUG),");
2927                s.concat(" -g");
2928                s.concat(",");
2929             }
2930             switch(options.optimization)
2931             {
2932                case speed: s.concat(" -O2"); break;
2933                case size: s.concat(" -Os"); break;
2934             }
2935             if(options.fastMath == true)
2936                s.concat(" -ffast-math");
2937             if(options.debug == true)
2938                s.concat(" -g");
2939             if(options.debug != true)
2940                s.concat(")");
2941          }
2942          else if(commonOptions)
2943             s.concat(" $(if $(DEBUG),-g)");
2944          if(commonOptions)
2945             s.concat(" $(FPIC)");
2946       }
2947       switch(options.warnings)
2948       {
2949          case all: s.concat(" -Wall"); break;
2950          case none: s.concat(" -w"); break;
2951       }
2952       if(options.profile)
2953          s.concat(" -pg");
2954    }
2955
2956    if(options && options.preprocessorDefinitions)
2957       ListOptionToDynamicString("D", options.preprocessorDefinitions, false, lineEach, "\t\t\t", false, s);
2958    if(options && options.includeDirs)
2959       ListOptionToDynamicString("I", options.includeDirs, true, lineEach, "\t\t\t", true, s);
2960 }
2961
2962 static void GenECFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, DynamicString s)
2963 {
2964    if(options.memoryGuard == true)
2965       s.concat(" -memguard");
2966    if(options.noLineNumbers == true)
2967       s.concat(" -nolinenumbers");
2968    if(options.strictNameSpaces == true)
2969       s.concat(" -strictns");
2970    if(options.defaultNameSpace && options.defaultNameSpace[0])
2971       s.concatf(" -defaultns %s", options.defaultNameSpace);
2972 }
2973
2974 static void ListOptionToDynamicString(char * option, Array<String> list, bool prioritize,
2975       ListOutputMethod method, String newLineStart, bool noSpace, DynamicString s)
2976 {
2977    if(list.count)
2978    {
2979       if(method == newLine)
2980       {
2981          s.concat(" \\\n");
2982          s.concat(newLineStart);
2983       }
2984       if(prioritize)
2985       {
2986          Map<String, int> sortedList { };
2987          MapNode<String, int> mn;
2988          for(item : list)
2989             sortedList[item] = 1;
2990          for(mn = sortedList.root.minimum; mn; mn = mn.next)
2991          {
2992             char * start = strstr(mn.key, "\n");
2993             if(method == lineEach)
2994             {
2995                s.concat(" \\\n");
2996                s.concat(newLineStart);
2997             }
2998             s.concat(" -");
2999             s.concat(option);
3000             if(noSpace)
3001                StringNoSpaceToDynamicString(s, start ? start+1 : mn.key);
3002             else
3003                s.concat(start ? start+1 : mn.key);
3004          }
3005          delete sortedList;
3006       }
3007       else
3008       {
3009          for(item : list)
3010          {
3011             if(method == lineEach)
3012             {
3013                s.concat(" \\\n");
3014                s.concat(newLineStart);
3015             }
3016             s.concat(" -");
3017             s.concat(option);
3018             if(noSpace)
3019                StringNoSpaceToDynamicString(s, item);
3020             else
3021                s.concat(item);
3022          }
3023       }
3024    }
3025 }
3026
3027 class GenericOptionTools<class X>
3028 {
3029    bool mergeValues, configReplaces;
3030
3031    virtual bool OptionSet(ProjectOptions options, int option) {
3032       if(*(X*)((byte *)options + option))
3033          return true;
3034       return false;
3035    }
3036
3037    // BUG: OptionCheck = OptionSet; // Overrides derived classes OptionCheck ?
3038
3039    virtual bool OptionCheck(ProjectOptions options, int option) {
3040       return OptionSet(options, option);
3041    }
3042
3043    virtual void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output);
3044    virtual void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output);
3045 }
3046
3047 class StringArrayOptionTools : GenericOptionTools<Array<String>>
3048 {
3049    bool caseSensitive;
3050 }
3051
3052 class NameCollisionInfo
3053 {
3054    bool ec;
3055    bool s;
3056    bool c;
3057    bool cpp;
3058    bool cc;
3059    bool cxx;
3060    bool m;
3061    bool mm;
3062    byte count;
3063
3064    bool IsExtensionColliding(char * extension)
3065    {
3066       bool colliding;
3067       if(count > 1 &&
3068             ((!strcmpi(extension, "c")   && ec) ||
3069              (!strcmpi(extension, "s")   && (ec || c)) ||
3070              (!strcmpi(extension, "cpp") && (ec || c || s)) ||
3071              (!strcmpi(extension, "cc")  && (ec || c || s || cpp)) ||
3072              (!strcmpi(extension, "cxx") && (ec || c || s || cpp || cc)) ||
3073              (!strcmpi(extension, "m")   && (ec || c || s || cpp || cc || m)) ||
3074               !strcmpi(extension, "mm")))
3075          colliding = true;
3076       else
3077          colliding = false;
3078      return colliding;
3079    }
3080 }