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