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