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