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