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