ide/Project: Fixed mixing up configurations/compilers
[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 bool eString_PathInsideOfMore(char * path, char * of, char * pathRest)
16 {
17    if(!path[0] || !of[0])
18       return false;  // What to do here? Ever used?
19    else
20    {
21       char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION];
22       char pathPart[MAX_FILENAME]; //, pathRest[MAX_LOCATION];
23       strcpy(ofRest, of);
24       strcpy(pathRest, path);
25       for(; ofRest[0] && pathRest[0];)
26       {
27          SplitDirectory(ofRest, ofPart, ofRest);      
28          SplitDirectory(pathRest, pathPart, pathRest);
29          if(fstrcmp(pathPart, ofPart))
30             return false;
31       }
32       if(!ofRest[0] && !pathRest[0])
33          return false;
34       else if(!pathRest[0])           // not inside of, it's the other way around
35          return false;
36       return true;
37    }
38 }
39
40 enum NodeTypes { project, file, folder, resources, folderOpen };
41 enum NodeIcons
42 {
43    genFile, ewsFile, epjFile, folder, openFolder, ecFile, ehFile,
44    cFile, hFile, cppFile, hppFile, textFile, webFile, pictureFile, soundFile,
45    archiveFile, packageFile, opticalMediaImageFile, mFile;
46
47    NodeIcons ::SelectFileIcon(char * filePath)
48    {
49       NodeIcons icon;
50       if(filePath && filePath[0])
51       {
52          char extension[MAX_EXTENSION];
53          GetExtension(filePath, extension);
54          if(strlen(extension))
55          {
56             if(!strcmpi(extension, WorkspaceExtension))
57                icon = ewsFile;
58             else if(!strcmpi(extension, ProjectExtension))
59                icon = epjFile;
60             else if(!strcmpi(extension, "ec"))
61                icon = ecFile;
62             else if(!strcmpi(extension, "eh"))
63                icon = ehFile;
64             else if(!strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
65                   !strcmpi(extension, "cxx"))
66                icon = cppFile;
67             else if(!strcmpi(extension, "hpp") || !strcmpi(extension, "hh") ||
68                   !strcmpi(extension, "hxx"))
69                icon = hppFile;
70             else if(!strcmpi(extension, "c"))
71                icon = cFile;
72             else if(!strcmpi(extension, "h"))
73                icon = hFile;
74             else if(!strcmpi(extension, "m"))
75                icon = mFile;
76             else if(!strcmpi(extension, "txt") || !strcmpi(extension, "text") ||
77                   !strcmpi(extension, "nfo") || !strcmpi(extension, "info"))
78                icon = textFile;
79             else if(!strcmpi(extension, "htm") || !strcmpi(extension, "html") ||
80                   !strcmpi(extension, "css") || !strcmpi(extension, "php") ||
81                   !strcmpi(extension, "js"))
82                icon = webFile;
83             else if(!strcmpi(extension, "bmp") || !strcmpi(extension, "pcx") ||
84                   !strcmpi(extension, "jpg") || !strcmpi(extension, "jpeg") ||
85                   !strcmpi(extension, "gif") || !strcmpi(extension, "png") ||
86                   !strcmpi(extension, "ico"))
87                icon = pictureFile;
88             else if(!strcmpi(extension, "wav") || !strcmpi(extension, "mp3") ||
89                   !strcmpi(extension, "ogg") || !strcmpi(extension, "snd"))
90                icon = soundFile;
91             else if(!strcmpi(extension, "ear") || !strcmpi(extension, "7z") ||
92                   !strcmpi(extension, "rar") || !strcmpi(extension, "zip") ||
93                   !strcmpi(extension, "gz") || !strcmpi(extension, "bz2") ||
94                   !strcmpi(extension, "tar") || !strcmpi(extension, "arj") ||
95                   !strcmpi(extension, "lza") || !strcmpi(extension, "lzh") ||
96                   !strcmpi(extension, "cpio") || !strcmpi(extension, "z"))
97                icon = archiveFile;
98             else if(!strcmpi(extension, "cab") || !strcmpi(extension, "deb") ||
99                   !strcmpi(extension, "rpm"))
100                icon = packageFile;
101             else if(!strcmpi(extension, "iso") || !strcmpi(extension, "mds") ||
102                   !strcmpi(extension, "cue") || !strcmpi(extension, "bin") ||
103                   !strcmpi(extension, "ccd") || !strcmpi(extension, "bwt") ||
104                   !strcmpi(extension, "cdi") || !strcmpi(extension, "nrg"))
105                icon = opticalMediaImageFile;
106             else
107                icon = genFile;
108          }
109          else
110             icon = genFile;
111       }
112       else
113          icon = genFile; // tocheck: error icon?
114       return icon;
115    }
116
117    NodeIcons ::SelectNodeIcon(NodeTypes type)
118    {
119       switch(type)
120       {
121          case project:
122             return epjFile;
123          case file:
124             return genFile;
125          case folder:
126             return folder;
127          case resources:
128             return archiveFile;
129          case folderOpen:
130             return openFolder;
131       }
132       return genFile;
133    }
134 };
135
136 #define SELECTION_COLOR Color { 10, 36, 106 }
137
138 // On Windows & UNIX
139 #define SEPS    "/"
140 #define SEP     '/'
141
142 // this is so not working, why!
143 //struct TwoStrings
144 // return result was not even executed (did not step on while debugging)
145 class TwoStrings : struct
146 {
147    char * a;
148    char * b;
149
150    property bool
151    {
152       get
153       {
154          return a && a[0];
155       }
156    }
157
158    ~TwoStrings()
159    {
160       delete a;
161       delete b;
162    }
163 }
164
165 class ProjectNode : ListItem
166 {
167 public:
168    property String
169    {
170       set { return { fileName = value }; }
171       // TOCHECK: Is this isset necessary at all?
172       isset { return nodeType == file && !options && !configurations && !platforms && !files; }
173    };
174    property String folder
175    {
176       set
177       {
178          nodeType = folder;
179          if(strchr(value, '/'))
180          {
181             char p[MAX_LOCATION];
182             char n[MAX_FILENAME];
183             GetLastDirectory(value, n);
184             StripLastDirectory(value, p);
185             name = CopyString(n);
186             path = CopyString(p);
187          }
188          else
189             name = CopyString(value);
190       }
191       get
192       {
193          // TOCHECK: Non Reentrant
194          static char insidePath[MAX_LOCATION];
195
196          strcpy(insidePath, (parent.type == project) ? "" : parent.path);
197          PathCatSlash(insidePath, name);
198
199          if(!fstrcmp(path, insidePath))
200             return name;
201          else
202          {
203             strcpy(insidePath, path);
204             if(!insidePath[0]) strcpy(insidePath, ".");
205             PathCatSlash(insidePath, name);
206             return insidePath;
207          }
208       }
209       isset { return nodeType == folder; }
210    };
211    property String fileName
212    {
213       set
214       {
215          nodeType = file;
216          if(strchr(value, '/'))
217          {
218             char p[MAX_LOCATION];
219             char n[MAX_FILENAME];
220             GetLastDirectory(value, n);
221             StripLastDirectory(value, p);
222             name = CopyValidateMakefilePath(n);
223             path = CopyValidateMakefilePath(p);
224          }
225          else
226             name = CopyValidateMakefilePath(value);
227       }
228       get
229       {
230          // TOCHECK: Non Reentrant
231          static char insidePath[MAX_LOCATION];
232
233          strcpy(insidePath, (parent.type == project) ? "" : parent.path);
234          if(!fstrcmp(path, insidePath))
235             return name;
236          else
237          {
238             strcpy(insidePath, path);
239             if(!insidePath[0]) strcpy(insidePath, ".");
240             PathCatSlash(insidePath, name);
241             return insidePath;
242          }
243       }
244       isset { return nodeType == file && (options || configurations || platforms); }
245    };
246
247    LinkList<ProjectNode> files;
248    property ProjectOptions options
249    {
250       get { return project ? project.options : options; }
251       set { if(project) project.options = value; else options = value; }
252       isset { ProjectOptions options = project ? project.options : this.options; return options && !options.isEmpty; }
253    }
254    property Array<PlatformOptions> platforms
255    {
256       get { return project ? project.platforms : platforms; }
257       set
258       {
259          if(project) { project.platforms = value; }
260          else
261          {
262             if(platforms) { platforms.Free(); delete platforms; }
263             if(value)
264             {
265                List<PlatformOptions> empty { };
266                Iterator<PlatformOptions> it { value };
267                platforms = value;
268                for(p : platforms; !p.options || p.options.isEmpty) empty.Add(p);
269                for(p : empty; it.Find(p)) platforms.Delete(it.pointer);
270                delete empty;
271             }
272          }
273       }
274       isset
275       {
276          Array<PlatformOptions> platforms = project ? project.platforms : this.platforms;
277          if(platforms)
278          {
279             for(p : platforms)
280             {
281                if(p.options && !p.options.isEmpty)
282                   return true;
283             }
284          }
285          return false;
286       }
287    }
288    property List<ProjectConfig> configurations
289    {
290       get { return project ? project.configurations : configurations; }
291       set
292       {
293          if(project) { project.configurations = value; }
294          else
295          {
296             if(configurations) { configurations.Free(); delete configurations; }
297             if(value)
298             {
299                List<ProjectConfig> empty { };
300                Iterator<ProjectConfig> it { value };
301                configurations = value;
302                for(c : configurations)
303                {
304                   bool somethingSet = c.options && !c.options.isEmpty;
305                   // TODO: Implement isset keyword
306                   if(!somethingSet && c.platforms && c.platforms.count)
307                   {
308                      for(p : c.platforms)
309                      {
310                         if(p.options && !p.options.isEmpty)
311                         {
312                            somethingSet = true;
313                            break;
314                         }
315                      }
316                   }
317                   if(!somethingSet)
318                      empty.Add(c);
319                }
320                for(c : empty; it.Find(c)) configurations.Delete(it.pointer);
321                delete empty;
322             }
323          }
324       }
325       isset
326       {
327          if(!parent) return true;
328          if(configurations)
329          {
330             for(c : configurations)
331             {
332                bool somethingSet = c.options && !c.options.isEmpty;
333                if(!somethingSet && c.platforms && c.platforms.count)
334                {
335                   for(p : c.platforms)
336                   {
337                      if(p.options && !p.options.isEmpty)
338                      {
339                         somethingSet = true;
340                         break;
341                      }
342                   }
343                }
344                return somethingSet;
345             }
346          }
347          return false;
348       }
349    }
350
351 private:
352    ProjectOptions options;
353    Array<PlatformOptions> platforms;
354    List<ProjectConfig> configurations;
355    ProjectNodeType nodeType;
356    ProjectNode parent;
357    char * name;
358    char * info;
359
360    // This holds the absolute path of the .epj for the project topnode (without the filename)
361    // It holds a relative path to the topNode (project) for other nodes (folders and files)
362    // For folders, it includes the folder it refers to. If there is a name difference between the
363    // file system folder and the grouping folder of the project view, it maps to that folder.
364    char * path;
365    
366    NodeTypes type;
367    NodeIcons icon;
368    int indent;
369    DataRow row;
370
371    bool modified;
372    
373    // This is only set for Top Nodes
374    Project project;
375
376    ProjectConfig GetMatchingNodeConfig(ProjectConfig prjConfig)
377    {
378       ProjectConfig nodeConfig = null;
379       if(property::configurations && prjConfig)
380       {
381          const char * configName = prjConfig.name;
382          for(cfg : property::configurations)
383          {
384             if(!strcmpi(cfg.name, configName))
385             {
386                nodeConfig = cfg;
387                break;
388             }
389          }
390       }
391       return nodeConfig;
392    }
393
394    // For makefile generation:
395    bool GetECFLAGS(ProjectConfig prjConfig)
396    {
397       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
398       ProjectOptions options = property::options;
399       SetBool memoryGuard = localMemoryGuard;
400       String defaultNameSpace = localDefaultNameSpace;
401       SetBool strictNameSpaces = localStrictNameSpaces;
402       SetBool noLineNumbers = localNoLineNumbers;
403
404       if(memoryGuard || defaultNameSpace || strictNameSpaces || noLineNumbers)
405          return true;
406       else if(parent.parent)
407          return parent.GetECFLAGS(prjConfig);
408       else
409          return false;
410    }
411    
412    bool GetMemoryGuard(ProjectConfig prjConfig)
413    {
414       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
415       ProjectOptions options = property::options;
416       SetBool memoryGuard = localMemoryGuard;
417       if(!memoryGuard)
418       {
419          if(parent)
420             return parent.GetMemoryGuard(prjConfig);
421       }
422       return memoryGuard == true;
423    }
424
425    String GetDefaultNameSpace(ProjectConfig prjConfig)
426    {
427       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
428       ProjectOptions options = property::options;
429       String defaultNameSpace = localDefaultNameSpace;
430       if(!defaultNameSpace)
431       {
432          if(parent)
433             return parent.GetDefaultNameSpace(prjConfig);
434       }
435       return defaultNameSpace;
436    }
437
438    bool GetStrictNameSpaces(ProjectConfig prjConfig)
439    {
440       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
441       ProjectOptions options = property::options;
442       SetBool strictNameSpaces = localStrictNameSpaces;
443       if(!strictNameSpaces)
444       {
445          if(parent)
446             return parent.GetStrictNameSpaces(prjConfig);
447       }
448       return strictNameSpaces == true;
449    }
450
451    bool GetNoLineNumbers(ProjectConfig prjConfig)
452    {
453       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
454       ProjectOptions options = property::options;
455       SetBool noLineNumbers = localNoLineNumbers;
456       if(!noLineNumbers)
457       {
458          if(parent)
459             return parent.GetNoLineNumbers(prjConfig);
460       }
461       return noLineNumbers == true;
462    }
463
464    property ProjectNode root { get { ProjectNode n; for(n = this; n.parent; n = n.parent); return n; } }
465
466    property bool containsFile
467    {
468       get
469       {
470          bool result;
471          if(files)
472          {
473             for(child : files)
474             {
475                if(child.type == file ||
476                      ((child.type == folder || child.type == folderOpen) && child.containsFile))
477                {
478                   result = true;
479                   break;
480                }
481             }
482          }
483          else
484             result = false;
485          return result;
486       }
487    }
488
489    char * GetFullFilePath(char * buffer)
490    {
491       if(buffer)
492       {
493          strcpy(buffer, root.path);
494          PathCatSlash(buffer, path);
495          PathCatSlash(buffer, name);
496       }
497       return buffer;
498    }
499
500    char * GetFileSysMatchingPath(char * buffer)
501    {
502       if(buffer)
503       {
504          ProjectNode n, root = this.root;
505          for(n = this; n && (n.type == folder || n.type == project); n = n.parent)
506          {
507             strcpy(buffer, root.path);
508             if(n != root)
509                PathCatSlash(buffer, n.path);
510             if(FileExists(buffer).isDirectory)
511                break;
512          }
513          if(!(n && (n.type == folder || n.type == project)))
514             buffer[0] = '\0';
515       }
516       return buffer;
517    }
518
519    void CollectPerFileAndDirOptions(ProjectConfig prjConfig, Array<String> perFilePreprocessorDefs, Array<DirPath> perFileIncludeDirs)
520    {
521       ProjectNode node = null;
522       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
523       List<ProjectNode> nodeStack { };
524       
525       for(node = this; node && node.parent; node = node.parent)
526          nodeStack.Add(node);
527
528       // Should we reverse this stack to give priority to the per-file includes? Does the following technique already reverse? 
529
530       // TODO: Check how to fix duplication of following options when configuration is made per-config-per-file
531       while((node = nodeStack.lastIterator.data))
532       {
533          ProjectConfig config = GetMatchingNodeConfig(prjConfig);
534          ProjectOptions nodeOptions = node.property::options;
535          if(nodeOptions && nodeOptions.preprocessorDefinitions)
536          {
537             for(def : nodeOptions.preprocessorDefinitions)
538                perFilePreprocessorDefs.Add(CopyString(def));
539          }
540          if(config && config.options && config.options.preprocessorDefinitions)
541          {
542             for(def : config.options.preprocessorDefinitions)
543                perFilePreprocessorDefs.Add(CopyString(def));
544          }
545          if(nodeOptions && nodeOptions.includeDirs)
546          {
547             for(dir : nodeOptions.includeDirs)
548                perFileIncludeDirs.Add(CopySystemPath(dir));
549          }
550          if(config && config.options && config.options.includeDirs)
551          {
552             for(dir : config.options.includeDirs)
553                perFileIncludeDirs.Add(CopySystemPath(dir));
554          }
555          nodeStack.lastIterator.Remove();
556       }
557       delete nodeStack;
558    }
559
560
561    property Project project
562    {
563       get
564       {
565          ProjectNode n = this;
566          while(n && n.type != project) n = n.parent;
567          return n ? (*&n.project) : null;
568       }
569    }   
570
571    void RenameConfig(char * oldName, char * newName)
572    {
573       if(files)
574       {
575          for(f : files; (f.configurations || f.files)) { f.RenameConfig(oldName, newName); }
576       }
577       if(property::configurations)
578       {
579          for(c : property::configurations; !strcmp(c.name, oldName))
580          {
581             delete c.name;
582             c.name = CopyString(newName);
583          }
584       }
585    }
586
587    void DeleteConfig(ProjectConfig configToDelete)
588    {
589       if(files)
590       {
591          for(f : files; (f.configurations || f.files)) { f.DeleteConfig(configToDelete); }
592       }
593       if(property::configurations)
594       {
595          Iterator<ProjectConfig> c { property::configurations };
596          while(c.Next())
597          {
598             ProjectConfig config = c.data;
599             if(!strcmp(configToDelete.name, config.name))
600             {               
601                c.Remove();
602                delete config;
603                break;
604             }
605          }
606          if(!property::configurations.count)
607             property::configurations = null;
608       }
609    }
610
611    ProjectNode Backup()
612    {
613       ProjectNode backupNode { };
614
615       if(files)
616       {
617          backupNode.files = { };
618          for(f : files) backupNode.files.Add(f.Backup());
619       }
620       if(property::options)
621          backupNode.options = property::options.Copy();
622
623       if(property::platforms)
624       {
625          backupNode.platforms = { };
626          for(p : property::platforms)
627             backupNode.platforms.Add(p.Copy());
628       }
629
630       if(property::configurations)
631       {
632          backupNode.configurations = { };
633          for(c : property::configurations)
634             backupNode.configurations.Add(c.Copy());
635       }
636       return backupNode;
637    }
638
639    void Revert(ProjectNode backupNode)
640    {
641       if(files)
642       {
643          Iterator<ProjectNode> it { backupNode.files };
644          for(f : files)
645          {
646             it.Next();
647             f.Revert(it.data);
648          }
649       }
650
651       property::options = backupNode.options ? backupNode.options.Copy() : null;
652       if(backupNode.platforms)
653       {
654          Array<PlatformOptions> platforms { };
655          property::platforms = platforms;
656
657          for(p : backupNode.platforms)
658             platforms.Add(p.Copy());
659       }
660       if(backupNode.configurations)
661       {
662          List<ProjectConfig> configurations { };
663          property::configurations = configurations;
664          for(c : backupNode.configurations)
665             configurations.Add(c.Copy());
666       }
667    }
668
669    void FixupNode(char * parentPath)
670    {
671       if(!parent)
672       {
673          type = project;
674       }
675       else if(nodeType == file)
676       {
677          type = file;
678          if(!path)
679          {
680             path = CopyString((parent.type == folder || parent.type == resources) ? parentPath : "");
681          }
682       }
683       else if(nodeType == folder)
684       {
685          type = folder;
686
687          if(!path)
688          {
689             char temp[MAX_LOCATION];
690             strcpy(temp, (parent.type == folder || parent.type == resources) ? parentPath : "");
691             PathCatSlash(temp, name);
692             path = CopyString(temp);
693          }
694       }
695
696       indent = parent ? parent.indent + 1 : 0;
697
698       if(type == file)
699          icon = NodeIcons::SelectFileIcon(name);
700       else
701          icon = NodeIcons::SelectNodeIcon(type);
702
703       if(files)
704       {
705          for(f : files)
706          {
707             f.parent = this;
708
709             if(type == project)
710                parentPath[0] = '\0';
711             else if(type == resources || type == folder)
712                strcpy(parentPath, path);
713
714             f.FixupNode(parentPath);
715          }
716       }
717    }
718
719    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
720    {
721       if(!needClass)
722       {
723          // TOCHECK: Called from JSON writer
724          if(nodeType == file && !property::options && !property::configurations && !property::platforms && name)
725          {
726             strcpy(tempString, "\"");
727             strcat(tempString, property::fileName);
728             strcat(tempString, "\"");
729             return tempString;
730          }
731          else
732             return null;
733       }
734       else
735          // TOCHECK: Called from ProjectView rendering
736          return name ? name : "";
737    }
738
739    ~ProjectNode()
740    {
741       if(files)
742       {
743          files.Free();
744          delete files;
745       }
746       if(!project)
747          delete options;
748
749       if(!project && platforms)
750       {
751          platforms.Free();
752          delete platforms;
753       };
754       if(!project && configurations)
755       {
756          configurations.Free();
757          delete configurations;
758       }
759
760       /////////////////////////////
761       delete path;
762       delete name;
763       delete info;
764    }
765
766    property bool isInResources
767    {
768       get
769       {
770          ProjectNode node;
771          for(node = this; node; node = node.parent)
772          {
773             if(node.type == resources)
774                return true;
775          }
776          return false;
777       }
778    }
779
780    TwoStrings GetPlatformSpecificFu(ProjectConfig prjConfig)
781    {
782       TwoStrings result { a = CopyString(""), b = CopyString("") };
783       // note: unknown platform is for common
784       Map<Platform, SetBool> exclusionInfo { };
785       MapNode<Platform, SetBool> mn;
786       char * exp, * var;
787       int len;
788       SetBool common;
789
790       CollectExclusionInfo(exclusionInfo, prjConfig);
791       common = exclusionInfo[unknown];
792       {
793          Map<Platform, SetBool> cleaned { };
794          SetBool opposite = common == true ? false : true;
795          for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
796          {
797             if(mn.key == unknown || mn.value == opposite)
798               cleaned[mn.key] = mn.value;
799          }
800          delete exclusionInfo;
801          exclusionInfo = cleaned;
802       }
803
804       if(exclusionInfo.count > 1)
805       {
806          if(exclusionInfo.count > 2)
807          {
808             exp = result.a;
809             len = strlen(exp) + strlen("$(if $(or ");
810             exp = renew exp char[len+1];
811             strcat(exp, "$(if $(or ");
812             result.a = exp;
813
814             for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
815             {
816                if(mn.key != unknown)
817                {
818                   char * comma = mn.next ? "," : "";
819
820                   var = PlatformToMakefileVariable(mn.key);
821
822                   exp = result.a;
823                   len = strlen(exp) + strlen("$(") + strlen(var) + strlen(")") + strlen(comma);
824                   exp = renew exp char[len+1];
825                   strcat(exp, "$(");
826                   strcat(exp, var);
827                   strcat(exp, ")");
828                   strcat(exp, comma);
829                   result.a = exp;
830                }
831             }
832
833             exp = result.a;
834             len = strlen(exp) + strlen("),");
835             exp = renew exp char[len+1];
836          }
837          else
838          {
839             if(exclusionInfo.root.minimum.key != unknown)
840                var = PlatformToMakefileVariable(exclusionInfo.root.minimum.key);
841             else
842                var = PlatformToMakefileVariable(exclusionInfo.root.minimum.next.key);
843
844             exp = result.a;
845             len = strlen(exp) + strlen("$(if $(") + strlen(var) + strlen("),");
846             exp = renew exp char[len+1];
847             strcat(exp, "$(if $(");
848             strcat(exp, var);
849          }
850
851          strcat(exp, "),");
852          result.a = exp;
853
854          exp = common == true ? result.b : result.a;
855          len = strlen(exp) + strlen(",");
856          exp = renew exp char[len+1];
857          strcat(exp, ",");
858          if(common == true) result.b = exp; else result.a = exp;
859
860          exp = result.b;
861          len = strlen(exp) + strlen(")");
862          exp = renew exp char[len+1];
863          strcat(exp, ")");
864          result.b = exp;
865       }
866       delete exclusionInfo;
867       
868       return result;
869    }
870
871    bool GetIsExcluded(ProjectConfig prjConfig)
872    {
873       bool result;
874       // note: unknown platform is for common
875       Map<Platform, SetBool> exclusionInfo { };
876       CollectExclusionInfo(exclusionInfo, prjConfig);
877       if(exclusionInfo.count == 0)
878          result = false;
879       else if(exclusionInfo.count == 1)
880          result = exclusionInfo.root.minimum.value == true;
881       else
882       {
883          SetBool check = exclusionInfo.root.minimum.value;
884          MapNode<Platform, SetBool> mn;
885          for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
886          {
887             if(check != mn.value)
888                break;
889          }
890          if(!mn) // all are same
891             result = check == true;
892          else
893             result = false;
894       }
895       delete exclusionInfo;
896       return result;
897    }
898
899    void CollectExclusionInfo(Map<Platform, SetBool> output, ProjectConfig prjConfig)
900    {
901       // note: unknown platform is for common
902       Platform platform;
903       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
904       ProjectOptions options = property::options;
905       Array<PlatformOptions> platforms = property::platforms;
906       List<ProjectConfig> configurations = property::configurations;
907
908       if(parent)
909          parent.CollectExclusionInfo(output, prjConfig);
910       else
911          output[unknown] = unset;
912
913       if(options && options.excludeFromBuild)
914          output[unknown] = options.excludeFromBuild;
915       
916       if(config && config.options && config.options.excludeFromBuild)
917          output[unknown] = config.options.excludeFromBuild;
918
919       if(platforms)
920       {
921          for(p : platforms)
922          {
923             if(p.options.excludeFromBuild && (platform = p.name))
924                output[platform] = p.options.excludeFromBuild;
925          }
926       }
927       if(config && config.platforms)
928       {
929          for(p : config.platforms)
930          {
931             if(p.options.excludeFromBuild && (platform = p.name))
932                output[platform] = p.options.excludeFromBuild;
933          }
934       }
935    }
936
937    void EnsureVisible()
938    {
939       if(parent)
940          parent.EnsureVisible();
941       row.collapsed = false;
942    }
943
944    void Delete()
945    {
946       if(parent)
947          parent.files.Delete(this);
948    }
949
950    ProjectNode Find(char * name, bool includeResources)
951    {
952       ProjectNode result = null;
953       if(files)
954       {
955          for(child : files)
956          {
957             if(includeResources || child.type != resources)
958             {
959                if(child.type != folder && child.name && !strcmpi(child.name, name))
960                {
961                   result = child;
962                   break;
963                }
964                result = child.Find(name, includeResources);
965                if(result)
966                   break;
967             }
968          }
969       }
970       return result;
971    }
972
973    ProjectNode FindWithPath(char * name, bool includeResources)
974    {
975       ProjectNode result = null;
976       if(files)
977       {
978          for(child : files)
979          {
980             if(includeResources || child.type != resources)
981             {
982                char path[MAX_LOCATION];
983                strcpy(path, child.path);
984                if(child.type != folder && child.name)
985                {
986                   PathCatSlash(path, child.name);
987                   if(!strcmpi(path, name))
988                   {
989                      result = child;
990                      break;
991                   }
992                }
993                result = child.FindWithPath(name, includeResources);
994                if(result)
995                   break;
996             }
997          }
998       }
999       return result;
1000    }
1001
1002    ProjectNode FindSpecial(char * name, bool recursive, bool includeResources, bool includeFolders)
1003    {
1004       ProjectNode result = null;
1005       if(files)
1006       {
1007          for(child : files)
1008          {
1009             if(includeResources || child.type != resources)
1010             {
1011                if((includeFolders || child.type != folder) && child.name && !strcmpi(child.name, name))
1012                {
1013                   result = child;
1014                   break;
1015                }
1016                if(recursive)
1017                   result = child.FindSpecial(name, recursive, includeResources, includeFolders);
1018                if(result)
1019                   break;
1020             }
1021          }
1022       }
1023       return result;
1024    }
1025
1026    ProjectNode FindSameNameConflict(char * name, bool includeResources,
1027       Map<Platform, SetBool> exclusionInfo, ProjectConfig prjConfig)
1028    {
1029       ProjectNode result = null;
1030       Map<Platform, SetBool> compareExclusion { };
1031       SetBool common, commonComp;
1032       SetBool actual, actualComp;
1033       if(files)
1034       {
1035          for(child : files)
1036          {
1037             if(includeResources || child.type != resources)
1038             {
1039                if(child.type != folder && child.name && !strcmpi(child.name, name))
1040                {
1041                   child.CollectExclusionInfo(compareExclusion, prjConfig);
1042                   common = exclusionInfo[unknown];
1043                   commonComp = compareExclusion[unknown];
1044                   if(exclusionInfo.count == 1 && compareExclusion.count == 1)
1045                   {
1046                      if(!(common == true || commonComp == true))
1047                      {
1048                         result = child;
1049                         break;
1050                      }
1051                   }
1052                   else
1053                   {
1054                      Platform platform;
1055                      for(platform = (Platform)1; platform < Platform::enumSize; platform++)
1056                      {
1057                         actual = common;
1058                         actualComp = commonComp;
1059                         if(exclusionInfo[platform] != unset)
1060                            actual = exclusionInfo[platform];
1061                         if(compareExclusion[platform] != unset)
1062                            actualComp = compareExclusion[platform];
1063                         if(!(actual == true || actualComp == true))
1064                         {
1065                            result = child;
1066                            break;
1067                         }
1068                      }
1069                      if(result) break;
1070                   }
1071                   compareExclusion.Free();
1072                   break;
1073                }
1074                result = child.FindSameNameConflict(name, includeResources, exclusionInfo, prjConfig);
1075                if(result) break;
1076             }
1077          }
1078          compareExclusion.Free();
1079       }
1080       delete compareExclusion;
1081       return result;
1082    }
1083
1084    ProjectNode Add(Project project, char * filePath, ProjectNode after, NodeTypes type, NodeIcons icon, bool checkIfExists)
1085    {
1086       ProjectNode node = null;
1087       char temp[MAX_LOCATION];
1088       Map<Platform, SetBool> exclusionInfo { };
1089
1090       GetLastDirectory(filePath, temp);
1091       //if(!checkIfExists || !project.topNode.Find(temp, false))
1092       
1093       // TOCHECK: Shouldn't this apply either for all configs or none?
1094       CollectExclusionInfo(exclusionInfo, project.config);
1095       if(!checkIfExists || !project.topNode.FindSameNameConflict(temp, false, exclusionInfo, project.config))
1096       {
1097          // Do the check for folder in the same parent or resource files only here
1098          if(type == folder || !checkIfExists)
1099          {
1100             for(node : files)
1101             {
1102                if(node.name && !strcmpi(node.name, temp))
1103                   return null;
1104             }
1105          }
1106
1107          node = ProjectNode { parent = this, indent = indent + 1, type = type, icon = icon, name = CopyString(temp) };
1108          if(type != file)
1109          {
1110             node.files = { }; 
1111             node.nodeType = folder;
1112          }
1113          if(type != folder)
1114          {
1115             if(filePath)
1116             {
1117                StripLastDirectory(filePath, temp);
1118                MakePathRelative(temp, project.topNode.path, temp);
1119                node.path = CopyUnixPath(temp);
1120             }
1121             node.nodeType = file;
1122          }
1123          else
1124          {
1125             strcpy(temp, (type == NodeTypes::project) ? "" : path);
1126             PathCatSlash(temp, node.name);
1127             node.path = CopyString(temp);
1128          }
1129          files.Insert(after, node);
1130       }
1131       delete exclusionInfo;
1132       return node;
1133    }
1134
1135 #ifndef MAKEFILE_GENERATOR
1136    void OnDisplay(Surface surface, int x, int y, int width, ProjectView projectView, Alignment alignment, DataDisplayFlags displayFlags)
1137    {
1138       char label[MAX_FILENAME];
1139       int indent = 16;
1140       int xStart;
1141       int len;
1142       int w, h;
1143       Bitmap bmp;
1144       bool showConfig = true;
1145
1146       if(!projectView)
1147       {
1148          showConfig = false;
1149          projectView = ide.projectView;
1150       }         
1151       
1152       bmp = projectView.icons[icon].bitmap;
1153       xStart = /*indent * indent + */x + (bmp ? (bmp.width + 5) : 0);
1154
1155       GetLastDirectory(name, label);
1156       if(!showConfig || projectView.drawingInProjectSettingsDialogHeader)
1157       {
1158          if(projectView.drawingInProjectSettingsDialogHeader || (type == project && info))
1159          {
1160             if(projectView.projectSettingsDialog && projectView.projectSettingsDialog.buildTab)
1161             {
1162                char * addendum;
1163                addendum = projectView.projectSettingsDialog.buildTab.selectedConfigName;
1164                if(strlen(addendum))
1165                {
1166                   strcat(label, " (");
1167                   strcat(label, addendum);
1168                   strcat(label, ")");
1169                }
1170                addendum = projectView.projectSettingsDialog.buildTab.selectedPlatformName;
1171                if(strlen(addendum))
1172                {
1173                   strcat(label, " (");
1174                   strcat(label, addendum);
1175                   strcat(label, ")");
1176                }
1177             }
1178          }
1179       }
1180       else if(!projectView.drawingInProjectSettingsDialog)
1181       {
1182          if(modified)
1183             strcat(label, " *");
1184          if(type == project && info)
1185          {
1186             int len = strlen(info) + 4;
1187             char * more = new char[len];
1188             sprintf(more, " (%s)", info);
1189             strcat(label, more);
1190             delete more;
1191          }
1192       }
1193       len = strlen(label);
1194       
1195       if(!bmp)
1196       {
1197          if(type == folder || type == folderOpen)
1198             surface.SetForeground(yellow);
1199          indent = 8;
1200       }
1201
1202       surface.TextOpacity(false);
1203       surface.TextExtent(label, len, &w, &h);
1204       h = Max(h, 16);
1205     
1206       // Draw the current row stipple
1207       if(displayFlags.selected)
1208          //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
1209          //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
1210          surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
1211       
1212       surface.WriteTextDots(alignment, xStart, y + 2, width, label, len);
1213       
1214       if(!app.textMode)
1215       {
1216          if(displayFlags.current)
1217          {
1218             if(displayFlags.active)
1219             {
1220                surface.LineStipple(0x5555);
1221                if(displayFlags.selected)
1222                   surface.SetForeground(projectView.fileList.stippleColor);
1223                else
1224                   surface.SetForeground(projectView.fileList.foreground);
1225             }
1226             else
1227             {
1228                surface.SetForeground(SELECTION_COLOR);
1229             }
1230             surface.Rectangle(xStart - 3, y, xStart + w + 1, y + h - 1);
1231             surface.LineStipple(0);
1232          }
1233
1234          if(bmp)
1235          {
1236             surface.SetForeground(white);
1237             surface.Blit(bmp, x /*+ indent * indent*/,y,0,0, bmp.width, bmp.height);
1238          }
1239       }
1240    }
1241 #endif
1242
1243    int OnCompare(ProjectNode b)
1244    {
1245       int result;
1246       if(type == b.type /*|| type >= TYPE_DRIVE*/)
1247          result = strcmpi(name, b.name);
1248       else
1249       {
1250          if(type == folder && b.type == file) result = -1;
1251          else if(type == file && b.type == folder) result = 1;
1252       }
1253       return result;
1254    }
1255
1256    void GenFileFlags(File f, Project project, ProjectConfig prjConfig)
1257    {
1258       ProjectNode node = null;
1259       List<ProjectNode> nodeStack { };
1260       
1261       for(node = this; node && node.parent; node = node.parent)
1262          nodeStack.Add(node);
1263
1264       // Should we reverse this stack to give priority to the per-file includes?
1265
1266       while((node = nodeStack.lastIterator.data))
1267       {
1268          ProjectOptions nodeOptions = node.property::options;
1269          ProjectConfig config = node.GetMatchingNodeConfig(prjConfig);
1270          if(nodeOptions && nodeOptions.preprocessorDefinitions)
1271             OutputListOption(f, "D", nodeOptions.preprocessorDefinitions, inPlace, false);
1272          if(config && config.options && config.options.preprocessorDefinitions)
1273             OutputListOption(f, "D", config.options.preprocessorDefinitions, inPlace, false);
1274          if(nodeOptions && nodeOptions.includeDirs)
1275             OutputListOption(f, "I", nodeOptions.includeDirs, inPlace, true);
1276          if(config && config.options && config.options.includeDirs)
1277             OutputListOption(f, "I", config.options.includeDirs, inPlace, true);
1278
1279          nodeStack.lastIterator.Remove();
1280       }
1281       delete nodeStack;
1282    }
1283
1284    void GenMakefileGetNameCollisionInfo(Map<String, NameCollisionInfo> namesInfo, ProjectConfig prjConfig)
1285    {
1286       if(type == file)
1287       {
1288          char extension[MAX_EXTENSION];
1289          GetExtension(name, extension);
1290          if(!strcmpi(extension, "ec") || !strcmpi(extension, "c") ||
1291                !strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
1292                !strcmpi(extension, "cxx") || !strcmpi(extension, "m"))
1293          {
1294             char moduleName[MAX_FILENAME];
1295             NameCollisionInfo info;
1296             ReplaceSpaces(moduleName, name);
1297             StripExtension(moduleName);
1298             info = namesInfo[moduleName];
1299             if(!info)
1300                info = NameCollisionInfo { };
1301             info.count++; // += 1; unless this is for a bug?
1302             if(!strcmpi(extension, "ec"))
1303                info.ec = true;
1304             else if(!strcmpi(extension, "c"))
1305                info.c = true;
1306             else if(!strcmpi(extension, "cpp"))
1307                info.cpp = true;
1308             else if(!strcmpi(extension, "cc"))
1309                info.cc = true;
1310             else if(!strcmpi(extension, "cxx"))
1311                info.cxx = true;
1312             else if(!strcmpi(extension, "m"))
1313                info.m = true;
1314             namesInfo[moduleName] = info;
1315          }
1316       }
1317       else if(files)
1318       {
1319          for(child : files)
1320          {
1321             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1322                child.GenMakefileGetNameCollisionInfo(namesInfo, prjConfig);
1323          }
1324       }
1325    }
1326    
1327    int GenMakefilePrintNode(File f, Project project, GenMakefilePrintTypes printType,
1328       Map<String, NameCollisionInfo> namesInfo, Array<String> items,
1329       ProjectConfig prjConfig)
1330    {
1331       int count = 0;
1332       if(type == file)
1333       {
1334          char s[2048];
1335          TwoStrings ts = GetPlatformSpecificFu(prjConfig);
1336          char moduleName[MAX_FILENAME];
1337          char extension[MAX_EXTENSION];
1338          GetExtension(name, extension);
1339          if(printType == resources)
1340          {
1341             bool useRes;
1342             char tempPath[MAX_LOCATION];
1343             char modulePath[MAX_LOCATION];
1344
1345             tempPath[0] = '\0';
1346             if(eString_PathInsideOfMore(path, project.resNode.path, tempPath))
1347             {
1348                useRes = true;
1349                PathCatSlash(tempPath, name);
1350             }
1351             else
1352             {
1353                useRes = false;
1354                strcpy(tempPath, path);
1355                PathCatSlash(tempPath, name);
1356             }
1357             ReplaceSpaces(modulePath, tempPath);
1358             sprintf(s, "%s%s%s%s", ts.a, useRes ? "$(RES)" : "", modulePath, ts.b);
1359             items.Add(CopyString(s));
1360          }
1361          else if(printType == sources)
1362          {
1363             if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1364                   !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1365                   !strcmpi(extension, "ec") || !strcmpi(extension, "m"))
1366             {
1367                char modulePath[MAX_LOCATION];
1368
1369                ReplaceSpaces(modulePath, path);
1370                ReplaceSpaces(moduleName, name);
1371                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
1372                items.Add(CopyString(s));
1373             }
1374          }
1375          else if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1376                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1377                !strcmpi(extension, "m"))
1378          {
1379             if(printType == objects)
1380             {
1381                bool collision;
1382                NameCollisionInfo info;
1383                ReplaceSpaces(moduleName, name);
1384                StripExtension(moduleName);
1385                info = namesInfo[moduleName];
1386                collision = info ? info.IsExtensionColliding(extension) : false;
1387                sprintf(s, "%s$(OBJ)%s%s%s.o%s", ts.a, moduleName, collision ? "." : "", collision ? extension : "", ts.b);
1388                items.Add(CopyString(s));
1389             }
1390          }
1391          else if(!strcmpi(extension, "ec"))
1392          {
1393             ReplaceSpaces(moduleName, name);
1394             StripExtension(moduleName);
1395             if(printType == objects)
1396                count++;
1397             s[0] = '\0';
1398             if(printType == objects)
1399                sprintf(s, "%s$(OBJ)%s.o%s", ts.a, moduleName, ts.b);
1400             else if(printType == cObjects)
1401                sprintf(s, "%s$(OBJ)%s.c%s", ts.a, moduleName, ts.b);
1402             else if(printType == symbols)
1403                sprintf(s, "%s$(OBJ)%s.sym%s", ts.a, moduleName, ts.b);
1404             else if(printType == imports)
1405                sprintf(s, "%s$(OBJ)%s.imp%s", ts.a, moduleName, ts.b);
1406             if(s[0])
1407                items.Add(CopyString(s));
1408          }
1409          delete ts;
1410       }
1411       else if(files)
1412       {
1413          for(child : files)
1414          {
1415             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1416                count += child.GenMakefilePrintNode(f, project, printType, namesInfo, items, prjConfig);
1417          }
1418       }
1419       return count;
1420    }
1421
1422    void GenMakefilePrintSymbolRules(File f, Project project, CompilerConfig compiler, ProjectConfig prjConfig)
1423    {
1424       //ProjectNode child;
1425       //char objDir[MAX_LOCATION];
1426       //ReplaceSpaces(objDir, config.objDir.dir);
1427
1428       //eSystem_Log("Printing Symbol Rules\n");
1429       if(type == file)
1430       {
1431          char extension[MAX_EXTENSION];
1432          char modulePath[MAX_LOCATION];
1433          char moduleName[MAX_FILENAME];
1434          
1435          GetExtension(name, extension);
1436          if(!strcmpi(extension, "ec"))
1437          {
1438             DualPipe dep;
1439             char command[2048];
1440
1441             ReplaceSpaces(moduleName, name);
1442             StripExtension(moduleName);
1443
1444             ReplaceSpaces(modulePath, path);
1445             if(modulePath[0]) strcat(modulePath, SEPS);
1446
1447 #if 0
1448             // *** Dependency command ***
1449             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s", moduleName,
1450                modulePath, moduleName, extension);
1451
1452             // System Includes (from global settings)
1453             for(item : compiler.dirs[Includes])
1454             {
1455                strcat(command, " -isystem ");
1456                if(strchr(item, ' '))
1457                {
1458                   strcat(command, "\"");
1459                   strcat(command, item);
1460                   strcat(command, "\"");
1461                }
1462                else
1463                   strcat(command, item);
1464             }
1465
1466             for(item : project.includeDirs)
1467             {
1468                strcat(command, " -I");
1469                if(strchr(item, ' '))
1470                {
1471                   strcat(command, "\"");
1472                   strcat(command, item);
1473                   strcat(command, "\"");
1474                }
1475                else
1476                   strcat(command, item);
1477             }
1478             for(item : project.preprocessorDefs)
1479             {
1480                strcat(command, " -D");
1481                strcat(command, item);
1482             }
1483
1484             // Execute it
1485             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1486             {
1487                char line[1024];
1488                bool firstLine = true;
1489                bool result = true;
1490
1491                // To do some time: auto save external dependencies?
1492                while(!dep.Eof())
1493                {
1494                   if(dep.GetLine(line, sizeof(line)-1))
1495                   {
1496                      if(firstLine)
1497                      {
1498                         char * colon = strstr(line, ":");
1499                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1500                         {
1501                            result = false;
1502                            break;
1503                         }
1504                         firstLine = false;
1505                      }
1506                      f.Puts(line);
1507                      f.Puts("\n");
1508                   }
1509                   if(!result) break;
1510                }
1511                delete dep;
1512
1513                // If we failed to generate dependencies...
1514                if(!result)
1515                {
1516 #endif
1517                   f.Printf("$(OBJ)%s.sym: %s%s.%s\n",
1518                      moduleName, modulePath, moduleName, extension);
1519 #if 0
1520                }
1521             }
1522 #endif
1523          /*
1524             f.Printf("\t$(ECP) %s%s.%s %s.sym\n\n",
1525                modulePath, moduleName, extension, moduleName);
1526             */
1527
1528             f.Printf("\t$(ECP)");
1529             // Give priority to file flags
1530             GenFileFlags(f, project, prjConfig);
1531
1532             f.Printf(" $(CECFLAGS)");
1533             if(GetECFLAGS(prjConfig))
1534             {
1535                if(GetMemoryGuard(prjConfig))
1536                   f.Printf(" -memguard");
1537                if(GetStrictNameSpaces(prjConfig))
1538                   f.Printf(" -strictns");
1539                {
1540                   char * s = GetDefaultNameSpace(prjConfig);
1541                   if(s && s[0])
1542                      f.Printf(" -defaultns %s", s);
1543                }
1544             }
1545             else
1546                f.Printf(" $(ECFLAGS)");
1547             f.Printf(" $(CFLAGS)");
1548
1549             f.Printf(" -c %s%s.%s -o $(OBJ)%s.sym\n\n",
1550                modulePath, moduleName, extension, moduleName);
1551          }
1552       }
1553       if(files)
1554       {
1555          for(child : files)
1556          {
1557             // TODO: Platform specific options
1558             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1559                child.GenMakefilePrintSymbolRules(f, project, compiler, prjConfig);
1560          }
1561       }
1562    }
1563
1564    void GenMakefilePrintCObjectRules(File f, Project project, CompilerConfig compiler, ProjectConfig prjConfig)
1565    {
1566       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1567       //ProjectNode child;
1568       //char objDir[MAX_LOCATION];
1569       //ReplaceSpaces(objDir, config.objDir.dir);
1570       //eSystem_Log("Printing C Object 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 #if 0
1590             // *** Dependency command ***
1591             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s",
1592                moduleName, modulePath, moduleName, extension);
1593
1594             // System Includes (from global settings)
1595             for(item : compiler.dirs[Includes])
1596             {
1597                strcat(command, " -isystem ");
1598                if(strchr(item, ' '))
1599                {
1600                   strcat(command, "\"");
1601                   strcat(command, item);
1602                   strcat(command, "\"");
1603                }
1604                else
1605                   strcat(command, item);
1606             }
1607
1608             for(item : config.includeDirs)
1609             {
1610                strcat(command, " -I");
1611                if(strchr(item, ' '))
1612                {
1613                   strcat(command, "\"");
1614                   strcat(command, item);
1615                   strcat(command, "\"");
1616                }
1617                else
1618                   strcat(command, item);
1619             }
1620             for(item : config.preprocessorDefs)
1621             {
1622                strcat(command, " -D");
1623                strcat(command, item);
1624             }
1625
1626             // Execute it
1627             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1628             {
1629                char line[1024];
1630                bool result = true;
1631                bool firstLine = true;
1632
1633                // To do some time: auto save external dependencies?
1634                while(!dep.Eof())
1635                {
1636                   if(dep.GetLine(line, sizeof(line)-1))
1637                   {
1638                      if(firstLine)
1639                      {
1640                         char * colon = strstr(line, ":");
1641                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1642                         {
1643                            result = false;
1644                            break;
1645                         }
1646                         firstLine = false;
1647                      }
1648                      f.Puts(line);
1649                      f.Puts("\n");
1650                   }
1651                   if(!result) break;
1652                }
1653                delete dep;
1654
1655                // If we failed to generate dependencies...
1656                if(!result)
1657                {
1658                   /* COMMENTED OUT FOR NOW
1659                   f.Printf("$(OBJ)%s.c: %s%s.%s $(Symbols)\n",
1660                      moduleName, modulePath, moduleName, extension);
1661                   */
1662 #endif
1663                   f.Printf("$(OBJ)%s.c: %s%s.%s $(OBJ)%s.sym | $(SYMBOLS)\n",
1664                      moduleName, modulePath, moduleName, extension, moduleName);
1665 #if 0
1666                }
1667             }
1668 #endif
1669          /*
1670             f.Printf("\t$(ECC) %s%s.%s $(OBJ)%s.c\n\n",
1671                modulePath, moduleName, extension, moduleName);
1672          */
1673
1674             f.Printf("\t$(ECC)");
1675             // Give priority to file flags
1676             GenFileFlags(f, project, prjConfig);
1677             if(GetECFLAGS(prjConfig))
1678             {
1679                f.Printf("%s $(CECFLAGS)", GetNoLineNumbers(prjConfig) ? " -nolinenumbers" : "");
1680                if(GetMemoryGuard(prjConfig))
1681                   f.Printf(" -memguard");
1682                if(GetStrictNameSpaces(prjConfig))
1683                   f.Printf(" -strictns");
1684                {
1685                   char * s = GetDefaultNameSpace(prjConfig);
1686                   if(s && s[0])
1687                      f.Printf(" -defaultns %s", s);
1688                }
1689             }
1690             else
1691                f.Printf(" $(CECFLAGS) $(ECFLAGS)");
1692             f.Printf(" $(CFLAGS) $(FVISIBILITY)");
1693
1694             f.Printf(" -c %s%s.%s -o $(OBJ)%s.c -symbols $(OBJ)\n\n",
1695                modulePath, moduleName, extension, moduleName);
1696          }
1697       }
1698       if(files)
1699       {
1700          for(child : files)
1701          {
1702             // TODO: Platform specific options
1703             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1704                child.GenMakefilePrintCObjectRules(f, project, compiler, prjConfig);
1705          }
1706       }
1707    }
1708
1709    void GenMakefilePrintObjectRules(File f, Project project,
1710       Map<String, NameCollisionInfo> namesInfo,
1711       CompilerConfig compiler, ProjectConfig prjConfig)
1712    {
1713       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1714       //ProjectNode child;
1715       //char objDir[MAX_LOCATION];
1716       //ReplaceSpaces(objDir, config.objDir.dir);
1717       //eSystem_Log("Printing Object Rules\n");
1718       if(type == file)
1719       {
1720          bool collision;
1721          char extension[MAX_EXTENSION];
1722          char modulePath[MAX_LOCATION];
1723          char moduleName[MAX_FILENAME];
1724          
1725          GetExtension(name, extension);
1726          /*if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1727                !strcmpi(extension, "ec") || !strcmpi(extension, "cc") ||
1728                !strcmpi(extension, "cxx"))*/
1729          if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1730                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1731                !strcmpi(extension, "m") || !strcmpi(extension, "ec"))
1732          {
1733             DualPipe dep;
1734             char command[2048];
1735             NameCollisionInfo info;
1736
1737             ReplaceSpaces(moduleName, name);
1738             StripExtension(moduleName);
1739
1740             info = namesInfo[moduleName];
1741             collision = info ? info.IsExtensionColliding(extension) : false;
1742             
1743             ReplaceSpaces(modulePath, path);
1744             if(modulePath[0]) strcat(modulePath, SEPS);
1745
1746             // *** Dependency command ***
1747             if(!strcmpi(extension, "ec"))
1748                sprintf(command, "%s -MT $(OBJ)%s.o -MM $(OBJ)%s.c", compiler.ccCommand, moduleName, moduleName);
1749             else
1750                sprintf(command, "%s -MT $(OBJ)%s.o -MM %s%s.%s", compiler.ccCommand, moduleName, modulePath, moduleName, extension);
1751
1752             if(!strcmpi(extension, "ec"))
1753                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1754             else
1755             {
1756 #if 0
1757                // System Includes (from global settings)
1758                for(item : compiler.dirs[includes])
1759                {
1760                   strcat(command, " -isystem ");
1761                   if(strchr(item, ' '))
1762                   {
1763                      strcat(command, "\"");
1764                      strcat(command, item);
1765                      strcat(command, "\"");
1766                   }
1767                   else
1768                      strcat(command, item);
1769                }
1770
1771                for(item : config.includeDirs)
1772                {
1773                   strcat(command, " -I");
1774                   if(strchr(item, ' '))
1775                   {
1776                      strcat(command, "\"");
1777                      strcat(command, item);
1778                      strcat(command, "\"");
1779                   }
1780                   else
1781                      strcat(command, item);
1782                }
1783                for(item : config.preprocessorDefs)
1784                {
1785                   strcat(command, " -D");
1786                   strcat(command, item);
1787                }
1788
1789                // Execute it
1790                if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1791                {
1792                   char line[1024];
1793                   bool firstLine = true;
1794                   bool result = true;
1795
1796                   // To do some time: auto save external dependencies?
1797
1798                   while(!dep.Eof())
1799                   {
1800                      if(dep.GetLine(line, sizeof(line)-1))
1801                      {
1802                         if(firstLine)
1803                         {
1804                            char * colon = strstr(line, ":");
1805                            if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1806                            {
1807                               result = false;
1808                               break;
1809                            }
1810                            firstLine = false;
1811                         }
1812                         f.Puts(line);
1813                         f.Puts("\n");
1814                      }
1815                      if(!result) break;
1816                   }
1817                   delete dep;
1818
1819                   // If we failed to generate dependencies...
1820                   if(!result)
1821                   {
1822 #endif
1823                      /*if(!strcmpi(extension, "ec"))
1824                         f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1825                      else*/
1826                         f.Printf("$(OBJ)%s%s%s.o: %s%s.%s\n", moduleName, 
1827                               collision ? "." : "", collision ? extension : "", modulePath, moduleName, extension);
1828 #if 0
1829                   }
1830                }
1831 #endif
1832             }
1833             f.Printf("\t$(CC)");
1834             // Give priority to file flags
1835             GenFileFlags(f, project, prjConfig);
1836
1837             f.Printf(" $(CFLAGS)");
1838
1839             if(!strcmpi(extension, "ec"))
1840                f.Printf(" $(FVISIBILITY) -c $(OBJ)%s.c -o $(OBJ)%s.o\n\n", moduleName, moduleName);
1841             else
1842                f.Printf(" -c %s%s.%s -o $(OBJ)%s%s%s.o\n\n",
1843                      modulePath, moduleName, !strcmpi(extension, "ec") ? "c" : extension, moduleName,
1844                      collision ? "." : "", collision ? extension : "");
1845          }
1846       }
1847       if(files)
1848       {
1849          for(child : files)
1850          {
1851             // TODO: Platform specific options
1852             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1853                child.GenMakefilePrintObjectRules(f, project, namesInfo, compiler, prjConfig);
1854          }
1855       }
1856    }
1857
1858    void GenMakefileAddResources(File f, String resourcesPath, ProjectConfig prjConfig)
1859    {
1860       int count = 0;
1861       if(files)
1862       {
1863          int c;
1864          bool prev = false;
1865          //Iterator<ProjectNode> i { files };
1866          //Iterator<ProjectNode> prev { files };
1867          //for(child : files)
1868          //while(i.Next())
1869          for(c = 0; c < files.count; c++)
1870          {
1871             ProjectNode child = files[c];
1872             TwoStrings ts = child.GetPlatformSpecificFu(prjConfig);
1873             if(count > 0 && ts)
1874                prev = true;
1875             if(child.type == file && !child.GetIsExcluded(prjConfig) && !(count > 0 && ts))
1876             {
1877                bool useRes;
1878                char tempPath[MAX_LOCATION];
1879                char resPath[MAX_LOCATION];
1880
1881                char * quotes;
1882
1883                // $(EAR) aw%s --- /*quiet ? "q" : */""
1884                if(count == 0)
1885                   f.Printf("\t%s$(EAR) aw $(TARGET)", ts.a);
1886
1887                tempPath[0] = '\0';
1888                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
1889                {
1890                   useRes = true;
1891                   PathCatSlash(tempPath, child.name);
1892                }
1893                else
1894                {
1895                   useRes = false;
1896                   strcpy(tempPath, child.path);
1897                   PathCatSlash(tempPath, child.name);
1898                }
1899                ReplaceSpaces(resPath, tempPath);
1900                if(strchr(tempPath, ' '))
1901                   quotes = "\"";
1902                else
1903                   quotes = "";
1904                f.Printf(" %s%s%s%s", quotes, useRes ? "$(RES)" : "", resPath, quotes);
1905                count++;
1906             }
1907             if(count == 10 || (count > 0 && (ts || !child.next)))
1908             {
1909                char path[MAX_LOCATION] = "", temp[MAX_LOCATION];
1910                ProjectNode parent;
1911
1912                for(parent = this; parent.type == folder; parent = parent.parent)
1913                {
1914                   strcpy(temp, path);
1915                   strcpy(path, parent.name);
1916                   if(temp[0])
1917                   {
1918                      strcat(path, "/");
1919                      strcat(path, temp);
1920                   }
1921                }
1922                f.Printf(" \"%s\"%s\n", path, ts.b);
1923                count = 0;
1924                if(prev)
1925                {
1926                   c--;
1927                   prev = false;
1928                }
1929             }
1930             delete ts;
1931          }
1932          for(child : files)
1933          {
1934             if(child.type == folder)
1935                child.GenMakefileAddResources(f, resourcesPath, prjConfig);
1936          }
1937       }
1938    }
1939 }
1940
1941 class NameCollisionInfo
1942 {
1943    bool ec;
1944    bool c;
1945    bool cpp;
1946    bool cc;
1947    bool cxx;
1948    bool m;
1949    byte count;
1950
1951    bool IsExtensionColliding(char * extension)
1952    {
1953       bool colliding;
1954       if(count > 1 && ((!strcmpi(extension, "c") && ec) ||
1955             (!strcmpi(extension, "cpp") && (ec || c)) ||
1956             (!strcmpi(extension, "cc") && (ec || c || cpp)) ||
1957             (!strcmpi(extension, "cxx") && (ec || c || cpp || cc)) ||
1958             !strcmpi(extension, "m")))
1959          colliding = true;
1960       else
1961          colliding = false;
1962      return colliding;
1963    }
1964 }