ide: proper fix for improved platform specific inclusion/exclusion
[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    ProjectOptions options;
249    Array<PlatformOptions> platforms;
250    List<ProjectConfig> configurations;
251
252    property ProjectConfig config
253    {
254       get
255       {
256          Project prj;
257          ProjectConfig result = null;
258          /*if(configurations)
259             printf("test\n");
260          if(prj)
261             printf("test\n");*/
262          if(configurations && (prj = property::project) && prj.config)
263          {
264             const char * projectConfigName = prj.config.name;
265             for(config : configurations)
266             {
267                if(!strcmpi(config.name, projectConfigName))
268                {
269                   result = config;
270                   break;
271                }
272             }
273          }
274          return result;
275       }
276    }
277
278    ProjectConfig GetMatchingNodeConfig(ProjectConfig config)
279    {
280       ProjectConfig nodeConfig = null;
281       if(configurations)
282       {
283          const char * configName = config.name;
284          for(cfg : configurations)
285          {
286             if(!strcmpi(cfg.name, configName))
287             {
288                nodeConfig = cfg;
289                break;
290             }
291          }
292       }
293       return nodeConfig;
294    }
295
296    property bool ecflags
297    {
298       get
299       {
300          ProjectConfig config = this.config;
301          ProjectOptions options = this.options;
302          SetBool memoryGuard = localMemoryGuard;
303          String defaultNameSpace = localDefaultNameSpace;
304          SetBool strictNameSpaces = localStrictNameSpaces;
305          SetBool noLineNumbers = localNoLineNumbers;
306
307          if(memoryGuard || defaultNameSpace || strictNameSpaces || noLineNumbers)
308             return true;
309          else if(parent.parent)
310             return parent.ecflags;
311          else
312             return false;
313       }
314    }
315    property bool memoryGuard
316    {
317       get
318       {
319          ProjectConfig config = this.config;
320          ProjectOptions options = this.options;
321          SetBool memoryGuard = localMemoryGuard;
322          if(!memoryGuard)
323          {
324             if(parent)
325                return parent.memoryGuard;
326          }
327          return memoryGuard == true;
328       }
329    }
330    property String defaultNameSpace
331    {
332       get
333       {
334          ProjectConfig config = this.config;
335          ProjectOptions options = this.options;
336          String defaultNameSpace = localDefaultNameSpace;
337          if(!defaultNameSpace)
338          {
339             if(parent)
340                return parent.defaultNameSpace;
341          }
342          return defaultNameSpace;
343       }
344    }
345    property bool strictNameSpaces
346    {
347       get
348       {
349          ProjectConfig config = this.config;
350          ProjectOptions options = this.options;
351          SetBool strictNameSpaces = localStrictNameSpaces;
352          if(!strictNameSpaces)
353          {
354             if(parent)
355                return parent.strictNameSpaces;
356          }
357          return strictNameSpaces == true;
358       }
359    }
360    property bool noLineNumbers
361    {
362       get
363       {
364          ProjectConfig config = this.config;
365          ProjectOptions options = this.options;
366          SetBool noLineNumbers = localNoLineNumbers;
367          if(!noLineNumbers)
368          {
369             if(parent)
370                return parent.noLineNumbers;
371          }
372          return noLineNumbers == true;
373       }
374    }
375
376    property ProjectNode root { get { ProjectNode n; for(n = this; n.parent; n = n.parent); return n; } }
377
378    property bool containsFile
379    {
380       get
381       {
382          bool result;
383          if(files)
384          {
385             for(child : files)
386             {
387                if(child.type == file ||
388                      ((child.type == folder || child.type == folderOpen) && child.containsFile))
389                {
390                   result = true;
391                   break;
392                }
393             }
394          }
395          else
396             result = false;
397          return result;
398       }
399    }
400
401    char * GetFullFilePath(char * buffer)
402    {
403       if(buffer)
404       {
405          strcpy(buffer, root.path);
406          PathCatSlash(buffer, path);
407          PathCatSlash(buffer, name);
408       }
409       return buffer;
410    }
411
412    char * GetFileSysMatchingPath(char * buffer)
413    {
414       if(buffer)
415       {
416          ProjectNode n, root = this.root;
417          for(n = this; n && (n.type == folder || n.type == project); n = n.parent)
418          {
419             strcpy(buffer, root.path);
420             if(n != root)
421                PathCatSlash(buffer, n.path);
422             if(FileExists(buffer).isDirectory)
423                break;
424          }
425          if(!(n && (n.type == folder || n.type == project)))
426             buffer[0] = '\0';
427       }
428       return buffer;
429    }
430
431    void CollectPerFileAndDirOptions(ProjectConfig projectConfig, Array<String> perFilePreprocessorDefs, Array<DirPath> perFileIncludeDirs)
432    {
433       ProjectNode node = null;
434       ProjectConfig config = GetMatchingNodeConfig(projectConfig);
435       List<ProjectNode> nodeStack { };
436       
437       for(node = this; node && node.parent; node = node.parent)
438          nodeStack.Add(node);
439
440       // Should we reverse this stack to give priority to the per-file includes? Does the following technique already reverse? 
441
442       // TODO: Check how to fix duplication of following options when configuration is made per-config-per-file
443       while((node = nodeStack.lastIterator.data))
444       {
445          if(node.options && node.options.preprocessorDefinitions)
446          {
447             for(def : node.options.preprocessorDefinitions)
448                perFilePreprocessorDefs.Add(CopyString(def));
449          }
450          if(config && config.options && config.options.preprocessorDefinitions)
451          {
452             for(def : config.options.preprocessorDefinitions)
453                perFilePreprocessorDefs.Add(CopyString(def));
454          }
455          if(node.options && node.options.includeDirs)
456          {
457             for(dir : node.options.includeDirs)
458                perFileIncludeDirs.Add(CopySystemPath(dir));
459          }
460          if(config && config.options && config.options.includeDirs)
461          {
462             for(dir : config.options.includeDirs)
463                perFileIncludeDirs.Add(CopySystemPath(dir));
464          }
465          nodeStack.lastIterator.Remove();
466       }
467       delete nodeStack;
468    }
469
470 private:
471    ProjectNodeType nodeType;
472    ProjectNode parent;
473    char * name;
474    char * info;
475
476    // This holds the absolute path of the .epj for the project topnode (without the filename)
477    // It holds a relative path to the topNode (project) for other nodes (folders and files)
478    // For folders, it includes the folder it refers to. If there is a name difference between the
479    // file system folder and the grouping folder of the project view, it maps to that folder.
480    char * path;
481    
482    NodeTypes type;
483    NodeIcons icon;
484    int indent;
485    DataRow row;
486
487    bool modified;
488    
489    // This is only set for Top Nodes
490    Project project;
491
492    property Project project
493    {
494       get
495       {
496          ProjectNode n = this;
497          while(n && n.type != project) n = n.parent;
498          return n ? (*&n.project) : null;
499       }
500    }   
501
502    void RenameConfig(char * oldName, char * newName)
503    {
504       if(files)
505       {
506          for(f : files; (f.configurations || f.files)) { f.RenameConfig(oldName, newName); }
507       }
508       if(configurations)
509       {
510          for(c : configurations; !strcmp(c.name, oldName))
511          {
512             delete c.name;
513             c.name = CopyString(newName);
514          }
515       }
516    }
517
518    void DeleteConfig(ProjectConfig configToDelete)
519    {
520       if(files)
521       {
522          for(f : files; (f.configurations || f.files)) { f.DeleteConfig(configToDelete); }
523       }
524       if(configurations)
525       {
526          Iterator<ProjectConfig> c { configurations };
527          while(c.Next())
528          {
529             ProjectConfig config = c.data;
530             if(!strcmp(configToDelete.name, config.name))
531             {               
532                c.Remove();
533                delete config;
534                break;
535             }
536          }
537          if(!configurations.count)
538          {
539             delete configurations;
540          }
541       }
542    }
543
544    ProjectNode Backup()
545    {
546       ProjectNode backupNode { };
547
548       if(files)
549       {
550          backupNode.files = { };
551          for(f : files) backupNode.files.Add(f.Backup());
552       }
553       if(options)
554          backupNode.options = options.Copy();
555
556       if(platforms)
557       {
558          backupNode.platforms = { };
559          for(p : platforms)
560             backupNode.platforms.Add(p.Copy());
561       }
562
563       if(configurations)
564       {
565          backupNode.configurations = { };
566          for(c : configurations)
567             backupNode.configurations.Add(c.Copy());
568       }
569       return backupNode;
570    }
571
572    void Revert(ProjectNode backupNode)
573    {
574       if(files)
575       {
576          Iterator<ProjectNode> it { backupNode.files };
577          for(f : files)
578          {
579             it.Next();
580             f.Revert(it.data);
581          }
582       }
583
584       delete options;
585       if(platforms)
586       {
587          platforms.Free();
588          delete platforms;
589       }
590       if(configurations)
591       {
592          configurations.Free();
593          delete configurations;
594       }
595
596       options = backupNode.options ? backupNode.options.Copy() : null;
597       if(backupNode.platforms)
598       {
599          platforms = { };
600          for(p : backupNode.platforms)
601             platforms.Add(p.Copy());
602       }
603       if(backupNode.configurations)
604       {
605          configurations = { };
606          for(c : backupNode.configurations)
607             configurations.Add(c.Copy());
608       }
609    }
610
611    void FixupNode(char * parentPath)
612    {
613       if(!parent)
614       {
615          type = project;
616       }
617       else if(nodeType == file)
618       {
619          type = file;
620          if(!path)
621          {
622             path = CopyString((parent.type == folder || parent.type == resources) ? parentPath : "");
623          }
624       }
625       else if(nodeType == folder)
626       {
627          type = folder;
628
629          if(!path)
630          {
631             char temp[MAX_LOCATION];
632             strcpy(temp, (parent.type == folder || parent.type == resources) ? parentPath : "");
633             PathCatSlash(temp, name);
634             path = CopyString(temp);
635          }
636       }
637
638       indent = parent ? parent.indent + 1 : 0;
639
640       if(type == file)
641          icon = NodeIcons::SelectFileIcon(name);
642       else
643          icon = NodeIcons::SelectNodeIcon(type);
644
645       if(files)
646       {
647          for(f : files)
648          {
649             f.parent = this;
650
651             if(type == project)
652                parentPath[0] = '\0';
653             else if(type == resources || type == folder)
654                strcpy(parentPath, path);
655
656             f.FixupNode(parentPath);
657          }
658       }
659    }
660
661    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
662    {
663       if(!needClass)
664       {
665          // TOCHECK: Called from JSON writer
666          if(nodeType == file && !options && !configurations && !platforms && name)
667          {
668             strcpy(tempString, "\"");
669             strcat(tempString, property::fileName);
670             strcat(tempString, "\"");
671             return tempString;
672          }
673          else
674             return null;
675       }
676       else
677          // TOCHECK: Called from ProjectView rendering
678          return name ? name : "";
679    }
680
681    ~ProjectNode()
682    {
683       if(files)
684       {
685          files.Free();
686          delete files;
687       }
688       delete options;
689
690       if(platforms)
691       {
692          platforms.Free();
693          delete platforms;
694       };
695       if(configurations)
696       {
697          configurations.Free();
698          delete configurations;
699       }
700
701       /////////////////////////////
702       delete path;
703       delete name;
704       delete info;
705    }
706
707    property bool isInResources
708    {
709       get
710       {
711          ProjectNode node;
712          for(node = this; node; node = node.parent)
713          {
714             if(node.type == resources)
715                return true;
716          }
717          return false;
718       }
719    }
720
721    property TwoStrings platformSpecificFu
722    {
723       get
724       {
725          TwoStrings result { a = CopyString(""), b = CopyString("") };
726          // note: unknown platform is for common
727          Map<Platform, SetBool> exclusionInfo { };
728          MapNode<Platform, SetBool> mn;
729          char * exp, * var;
730          int len;
731          SetBool common;
732
733          CollectExclusionInfo(exclusionInfo);
734          common = exclusionInfo[unknown];
735          {
736             Map<Platform, SetBool> cleaned { };
737             SetBool opposite = common == true ? false : true;
738             for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
739             {
740                if(mn.key == unknown || mn.value == opposite)
741                  cleaned[mn.key] = mn.value;
742             }
743             delete exclusionInfo;
744             exclusionInfo = cleaned;
745          }
746
747          if(exclusionInfo.count > 1)
748          {
749             if(exclusionInfo.count > 2)
750             {
751                exp = result.a;
752                len = strlen(exp) + strlen("$(if $(or ");
753                exp = renew exp char[len+1];
754                strcat(exp, "$(if $(or ");
755                result.a = exp;
756
757                for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
758                {
759                   if(mn.key != unknown)
760                   {
761                      char * comma = mn.next ? "," : "";
762
763                      var = PlatformToMakefileVariable(mn.key);
764
765                      exp = result.a;
766                      len = strlen(exp) + strlen("$(") + strlen(var) + strlen(")") + strlen(comma);
767                      exp = renew exp char[len+1];
768                      strcat(exp, "$(");
769                      strcat(exp, var);
770                      strcat(exp, ")");
771                      strcat(exp, comma);
772                      result.a = exp;
773                   }
774                }
775
776                exp = result.a;
777                len = strlen(exp) + strlen("),");
778                exp = renew exp char[len+1];
779             }
780             else
781             {
782                if(exclusionInfo.root.minimum.key != unknown)
783                   var = PlatformToMakefileVariable(exclusionInfo.root.minimum.key);
784                else
785                   var = PlatformToMakefileVariable(exclusionInfo.root.minimum.next.key);
786
787                exp = result.a;
788                len = strlen(exp) + strlen("$(if $(") + strlen(var) + strlen("),");
789                exp = renew exp char[len+1];
790                strcat(exp, "$(if $(");
791                strcat(exp, var);
792             }
793
794             strcat(exp, "),");
795             result.a = exp;
796
797             exp = common == true ? result.b : result.a;
798             len = strlen(exp) + strlen(",");
799             exp = renew exp char[len+1];
800             strcat(exp, ",");
801             if(common == true) result.b = exp; else result.a = exp;
802
803             exp = result.b;
804             len = strlen(exp) + strlen(")");
805             exp = renew exp char[len+1];
806             strcat(exp, ")");
807             result.b = exp;
808          }
809          
810          return result;
811       }
812    }
813
814    property bool isExcluded
815    {
816       get
817       {
818          bool result;
819          // note: unknown platform is for common
820          Map<Platform, SetBool> exclusionInfo { };
821          CollectExclusionInfo(exclusionInfo);
822          if(exclusionInfo.count == 0)
823             result = false;
824          else if(exclusionInfo.count == 1)
825             result = exclusionInfo.root.minimum.value == true;
826          else
827          {
828             SetBool check = exclusionInfo.root.minimum.value;
829             MapNode<Platform, SetBool> mn;
830             for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
831             {
832                if(check != mn.value)
833                   break;
834             }
835             if(!mn) // all are same
836                result = check == true;
837             else
838                result = false;
839          }
840          delete exclusionInfo;
841          return result;
842
843       }
844    }
845
846    void CollectExclusionInfo(Map<Platform, SetBool> output)
847    {
848       // note: unknown platform is for common
849       Platform platform;
850       ProjectConfig config = property::config;
851
852       if(parent)
853          parent.CollectExclusionInfo(output);
854       else
855          output[unknown] = unset;
856
857       if(options && options.excludeFromBuild)
858          output[unknown] = options.excludeFromBuild;
859       
860       if(config && config.options && config.options.excludeFromBuild)
861          output[unknown] = config.options.excludeFromBuild;
862
863       if(platforms)
864       {
865          for(p : platforms)
866          {
867             if(p.options.excludeFromBuild && (platform = p.name))
868                output[platform] = p.options.excludeFromBuild;
869          }
870       }
871       if(config && config.platforms)
872       {
873          for(p : config.platforms)
874          {
875             if(p.options.excludeFromBuild && (platform = p.name))
876                output[platform] = p.options.excludeFromBuild;
877          }
878       }
879    }
880
881    void EnsureVisible()
882    {
883       if(parent)
884          parent.EnsureVisible();
885       row.collapsed = false;
886    }
887
888    void Delete()
889    {
890       if(parent)
891          parent.files.Delete(this);
892    }
893
894    ProjectNode Find(char * name, bool includeResources)
895    {
896       ProjectNode result = null;
897       if(files)
898       {
899          for(child : files)
900          {
901             if(includeResources || child.type != resources)
902             {
903                if(child.type != folder && child.name && !strcmpi(child.name, name))
904                {
905                   result = child;
906                   break;
907                }
908                result = child.Find(name, includeResources);
909                if(result)
910                   break;
911             }
912          }
913       }
914       return result;
915    }
916
917    ProjectNode FindWithPath(char * name, bool includeResources)
918    {
919       ProjectNode result = null;
920       if(files)
921       {
922          for(child : files)
923          {
924             if(includeResources || child.type != resources)
925             {
926                char path[MAX_LOCATION];
927                strcpy(path, child.path);
928                if(child.type != folder && child.name)
929                {
930                   PathCatSlash(path, child.name);
931                   if(!strcmpi(path, name))
932                   {
933                      result = child;
934                      break;
935                   }
936                }
937                result = child.FindWithPath(name, includeResources);
938                if(result)
939                   break;
940             }
941          }
942       }
943       return result;
944    }
945
946    ProjectNode FindSpecial(char * name, bool recursive, bool includeResources, bool includeFolders)
947    {
948       ProjectNode result = null;
949       if(files)
950       {
951          for(child : files)
952          {
953             if(includeResources || child.type != resources)
954             {
955                if((includeFolders || child.type != folder) && child.name && !strcmpi(child.name, name))
956                {
957                   result = child;
958                   break;
959                }
960                if(recursive)
961                   result = child.FindSpecial(name, recursive, includeResources, includeFolders);
962                if(result)
963                   break;
964             }
965          }
966       }
967       return result;
968    }
969
970    ProjectNode Add(Project project, char * filePath, ProjectNode after, NodeTypes type, NodeIcons icon, bool checkIfExists)
971    {
972       ProjectNode node = null;
973       char temp[MAX_LOCATION];
974
975       GetLastDirectory(filePath, temp);
976       if(!checkIfExists || !project.topNode.Find(temp, false))
977       {
978          // Do the check for folder in the same parent or resource files only here
979          if(type == folder || !checkIfExists)
980          {
981             for(node : files)
982             {
983                if(node.name && !strcmpi(node.name, temp))
984                   return null;
985             }
986          }
987
988          node = ProjectNode { parent = this, indent = indent + 1, type = type, icon = icon, name = CopyString(temp) };
989          if(type != file)
990          {
991             node.files = { }; 
992             node.nodeType = folder;
993          }
994          if(type != folder)
995          {
996             if(filePath)
997             {
998                StripLastDirectory(filePath, temp);
999                MakePathRelative(temp, project.topNode.path, temp);
1000                node.path = CopyUnixPath(temp);
1001             }
1002             node.nodeType = file;
1003          }
1004          else
1005          {
1006             strcpy(temp, (type == NodeTypes::project) ? "" : path);
1007             PathCatSlash(temp, node.name);
1008             node.path = CopyString(temp);
1009          }
1010          files.Insert(after, node);
1011       }
1012       return node;
1013    }
1014
1015 #ifndef MAKEFILE_GENERATOR
1016    void OnDisplay(Surface surface, int x, int y, int width, ProjectView projectView, Alignment alignment, DataDisplayFlags displayFlags)
1017    {
1018       char label[MAX_FILENAME];
1019       int indent = 16;
1020       int xStart;
1021       int len;
1022       int w, h;
1023       Bitmap bmp;
1024       bool showConfig = true;
1025
1026       if(!projectView)
1027       {
1028          showConfig = false;
1029          projectView = ide.projectView;
1030       }         
1031       
1032       bmp = projectView.icons[icon].bitmap;
1033       xStart = /*indent * indent + */x + (bmp ? (bmp.width + 5) : 0);
1034
1035       GetLastDirectory(name, label);
1036       if(!showConfig || projectView.drawingInProjectSettingsDialogHeader)
1037       {
1038          if(projectView.drawingInProjectSettingsDialogHeader || (type == project && info))
1039          {
1040             if(projectView.projectSettingsDialog && projectView.projectSettingsDialog.buildTab)
1041             {
1042                char * addendum;
1043                addendum = projectView.projectSettingsDialog.buildTab.selectedConfigName;
1044                if(strlen(addendum))
1045                {
1046                   strcat(label, " (");
1047                   strcat(label, addendum);
1048                   strcat(label, ")");
1049                }
1050                addendum = projectView.projectSettingsDialog.buildTab.selectedPlatformName;
1051                if(strlen(addendum))
1052                {
1053                   strcat(label, " (");
1054                   strcat(label, addendum);
1055                   strcat(label, ")");
1056                }
1057             }
1058          }
1059       }
1060       else if(!projectView.drawingInProjectSettingsDialog)
1061       {
1062          if(modified)
1063             strcat(label, " *");
1064          if(type == project && info)
1065          {
1066             int len = strlen(info) + 4;
1067             char * more = new char[len];
1068             sprintf(more, " (%s)", info);
1069             strcat(label, more);
1070             delete more;
1071          }
1072       }
1073       len = strlen(label);
1074       
1075       if(!bmp)
1076       {
1077          if(type == folder || type == folderOpen)
1078             surface.SetForeground(yellow);
1079          indent = 8;
1080       }
1081
1082       surface.TextOpacity(false);
1083       surface.TextExtent(label, len, &w, &h);
1084       h = Max(h, 16);
1085     
1086       // Draw the current row stipple
1087       if(displayFlags.selected)
1088          //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
1089          //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
1090          surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
1091       
1092       surface.WriteTextDots(alignment, xStart, y + 2, width, label, len);
1093       
1094       if(!app.textMode)
1095       {
1096          if(displayFlags.current)
1097          {
1098             if(displayFlags.active)
1099             {
1100                surface.LineStipple(0x5555);
1101                if(displayFlags.selected)
1102                   surface.SetForeground(projectView.fileList.stippleColor);
1103                else
1104                   surface.SetForeground(projectView.fileList.foreground);
1105             }
1106             else
1107             {
1108                surface.SetForeground(SELECTION_COLOR);
1109             }
1110             surface.Rectangle(xStart - 3, y, xStart + w + 1, y + h - 1);
1111             surface.LineStipple(0);
1112          }
1113
1114          if(bmp)
1115          {
1116             surface.SetForeground(white);
1117             surface.Blit(bmp, x /*+ indent * indent*/,y,0,0, bmp.width, bmp.height);
1118          }
1119       }
1120    }
1121 #endif
1122
1123    int OnCompare(ProjectNode b)
1124    {
1125       int result;
1126       if(type == b.type /*|| type >= TYPE_DRIVE*/)
1127          result = strcmpi(name, b.name);
1128       else
1129       {
1130          if(type == folder && b.type == file) result = -1;
1131          else if(type == file && b.type == folder) result = 1;
1132       }
1133       return result;
1134    }
1135
1136    void GenFileFlags(File f, Project project)
1137    {
1138       ProjectNode node = null;
1139       List<ProjectNode> nodeStack { };
1140       
1141       for(node = this; node && node.parent; node = node.parent)
1142          nodeStack.Add(node);
1143
1144       // Should we reverse this stack to give priority to the per-file includes?
1145
1146       while((node = nodeStack.lastIterator.data))
1147       {
1148          ProjectConfig config = node.config;
1149          if(node.options && node.options.preprocessorDefinitions)
1150             OutputListOption(f, "D", node.options.preprocessorDefinitions, inPlace, false);
1151          if(config && config.options && config.options.preprocessorDefinitions)
1152             OutputListOption(f, "D", config.options.preprocessorDefinitions, inPlace, false);
1153          if(node.options && node.options.includeDirs)
1154             OutputListOption(f, "I", node.options.includeDirs, inPlace, true);
1155          if(config && config.options && config.options.includeDirs)
1156             OutputListOption(f, "I", config.options.includeDirs, inPlace, true);
1157
1158          nodeStack.lastIterator.Remove();
1159       }
1160       delete nodeStack;
1161    }
1162
1163    void GenMakefileGetNameCollisionInfo(Map<String, NameCollisionInfo> namesInfo)
1164    {
1165       if(type == file)
1166       {
1167          char extension[MAX_EXTENSION];
1168          GetExtension(name, extension);
1169          if(!strcmpi(extension, "ec") || !strcmpi(extension, "c") ||
1170                !strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
1171                !strcmpi(extension, "cxx") || !strcmpi(extension, "m"))
1172          {
1173             char moduleName[MAX_FILENAME];
1174             NameCollisionInfo info;
1175             ReplaceSpaces(moduleName, name);
1176             StripExtension(moduleName);
1177             info = namesInfo[moduleName];
1178             if(!info)
1179                info = NameCollisionInfo { };
1180             info.count++; // += 1; unless this is for a bug?
1181             if(!strcmpi(extension, "ec"))
1182                info.ec = true;
1183             else if(!strcmpi(extension, "c"))
1184                info.c = true;
1185             else if(!strcmpi(extension, "cpp"))
1186                info.cpp = true;
1187             else if(!strcmpi(extension, "cc"))
1188                info.cc = true;
1189             else if(!strcmpi(extension, "cxx"))
1190                info.cxx = true;
1191             else if(!strcmpi(extension, "m"))
1192                info.m = true;
1193             namesInfo[moduleName] = info;
1194          }
1195       }
1196       else if(files)
1197       {
1198          for(child : files)
1199          {
1200             if(child.type != resources && (child.type == folder || !child.isExcluded))
1201                child.GenMakefileGetNameCollisionInfo(namesInfo);
1202          }
1203       }
1204    }
1205    
1206    int GenMakefilePrintNode(File f, Project project, GenMakefilePrintTypes printType, Map<String, NameCollisionInfo> namesInfo, Array<String> items)
1207    {
1208       int count = 0;
1209       if(type == file)
1210       {
1211          char s[2048];
1212          TwoStrings ts = platformSpecificFu;
1213          char moduleName[MAX_FILENAME];
1214          char extension[MAX_EXTENSION];
1215          GetExtension(name, extension);
1216          if(printType == resources)
1217          {
1218             bool useRes;
1219             char tempPath[MAX_LOCATION];
1220             char modulePath[MAX_LOCATION];
1221
1222             tempPath[0] = '\0';
1223             if(eString_PathInsideOfMore(path, project.resNode.path, tempPath))
1224             {
1225                useRes = true;
1226                PathCatSlash(tempPath, name);
1227             }
1228             else
1229             {
1230                useRes = false;
1231                strcpy(tempPath, path);
1232                PathCatSlash(tempPath, name);
1233             }
1234             ReplaceSpaces(modulePath, tempPath);
1235             sprintf(s, "%s%s%s%s", ts.a, useRes ? "$(RES)" : "", modulePath, ts.b);
1236             items.Add(CopyString(s));
1237          }
1238          else if(printType == sources)
1239          {
1240             if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1241                   !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1242                   !strcmpi(extension, "ec") || !strcmpi(extension, "m"))
1243             {
1244                char modulePath[MAX_LOCATION];
1245
1246                ReplaceSpaces(modulePath, path);
1247                ReplaceSpaces(moduleName, name);
1248                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
1249                items.Add(CopyString(s));
1250             }
1251          }
1252          else if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1253                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1254                !strcmpi(extension, "m"))
1255          {
1256             if(printType == objects)
1257             {
1258                bool collision;
1259                NameCollisionInfo info;
1260                ReplaceSpaces(moduleName, name);
1261                StripExtension(moduleName);
1262                info = namesInfo[moduleName];
1263                collision = info ? info.IsExtensionColliding(extension) : false;
1264                sprintf(s, "%s$(OBJ)%s%s%s.o%s", ts.a, moduleName, collision ? "." : "", collision ? extension : "", ts.b);
1265                items.Add(CopyString(s));
1266             }
1267          }
1268          else if(!strcmpi(extension, "ec"))
1269          {
1270             ReplaceSpaces(moduleName, name);
1271             StripExtension(moduleName);
1272             if(printType == objects)
1273                count++;
1274             s[0] = '\0';
1275             if(printType == objects)
1276                sprintf(s, "%s$(OBJ)%s.o%s", ts.a, moduleName, ts.b);
1277             else if(printType == cObjects)
1278                sprintf(s, "%s$(OBJ)%s.c%s", ts.a, moduleName, ts.b);
1279             else if(printType == symbols)
1280                sprintf(s, "%s$(OBJ)%s.sym%s", ts.a, moduleName, ts.b);
1281             else if(printType == imports)
1282                sprintf(s, "%s$(OBJ)%s.imp%s", ts.a, moduleName, ts.b);
1283             if(s[0])
1284                items.Add(CopyString(s));
1285          }
1286          delete ts;
1287       }
1288       else if(files)
1289       {
1290          for(child : files)
1291          {
1292             if(child.type != resources && (child.type == folder || !child.isExcluded))
1293                count += child.GenMakefilePrintNode(f, project, printType, namesInfo, items);
1294          }
1295       }
1296       return count;
1297    }
1298
1299    void GenMakefilePrintSymbolRules(File f, Project project)
1300    {
1301       //ProjectNode child;
1302       //char objDir[MAX_LOCATION];
1303       CompilerConfig compiler = GetCompilerConfig();
1304       //ReplaceSpaces(objDir, project.config.objDir.dir);
1305
1306       //eSystem_Log("Printing Symbol Rules\n");
1307       if(type == file)
1308       {
1309          char extension[MAX_EXTENSION];
1310          char modulePath[MAX_LOCATION];
1311          char moduleName[MAX_FILENAME];
1312          
1313          GetExtension(name, extension);
1314          if(!strcmpi(extension, "ec"))
1315          {
1316             DualPipe dep;
1317             char command[2048];
1318
1319             ReplaceSpaces(moduleName, name);
1320             StripExtension(moduleName);
1321
1322             ReplaceSpaces(modulePath, path);
1323             if(modulePath[0]) strcat(modulePath, SEPS);
1324
1325 #if 0
1326             // *** Dependency command ***
1327             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s", moduleName,
1328                modulePath, moduleName, extension);
1329
1330             // System Includes (from global settings)
1331             for(item : compiler.dirs[Includes])
1332             {
1333                strcat(command, " -isystem ");
1334                if(strchr(item, ' '))
1335                {
1336                   strcat(command, "\"");
1337                   strcat(command, item);
1338                   strcat(command, "\"");
1339                }
1340                else
1341                   strcat(command, item);
1342             }
1343
1344             for(item : project.includeDirs)
1345             {
1346                strcat(command, " -I");
1347                if(strchr(item, ' '))
1348                {
1349                   strcat(command, "\"");
1350                   strcat(command, item);
1351                   strcat(command, "\"");
1352                }
1353                else
1354                   strcat(command, item);
1355             }
1356             for(item : project.preprocessorDefs)
1357             {
1358                strcat(command, " -D");
1359                strcat(command, item);
1360             }
1361
1362             // Execute it
1363             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1364             {
1365                char line[1024];
1366                bool firstLine = true;
1367                bool result = true;
1368
1369                // To do some time: auto save external dependencies?
1370                while(!dep.Eof())
1371                {
1372                   if(dep.GetLine(line, sizeof(line)-1))
1373                   {
1374                      if(firstLine)
1375                      {
1376                         char * colon = strstr(line, ":");
1377                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1378                         {
1379                            result = false;
1380                            break;
1381                         }
1382                         firstLine = false;
1383                      }
1384                      f.Puts(line);
1385                      f.Puts("\n");
1386                   }
1387                   if(!result) break;
1388                }
1389                delete dep;
1390
1391                // If we failed to generate dependencies...
1392                if(!result)
1393                {
1394 #endif
1395                   f.Printf("$(OBJ)%s.sym: %s%s.%s\n",
1396                      moduleName, modulePath, moduleName, extension);
1397 #if 0
1398                }
1399             }
1400 #endif
1401          /*
1402             f.Printf("\t$(ECP) %s%s.%s %s.sym\n\n",
1403                modulePath, moduleName, extension, moduleName);
1404             */
1405
1406             f.Printf("\t$(ECP) $(CECFLAGS)");
1407             if(ecflags)
1408             {
1409                if(memoryGuard)
1410                   f.Printf(" -memguard");
1411                if(strictNameSpaces)
1412                   f.Printf(" -strictns");
1413                {
1414                   char * s = defaultNameSpace;
1415                   if(s && s[0])
1416                      f.Printf(" -defaultns %s", s);
1417                }
1418             }
1419             else
1420                f.Printf(" $(ECFLAGS)");
1421             f.Printf(" $(CFLAGS)");
1422             GenFileFlags(f, project);
1423             f.Printf(" -c %s%s.%s -o $(OBJ)%s.sym\n\n",
1424                modulePath, moduleName, extension, moduleName);
1425          }
1426       }
1427       if(files)
1428       {
1429          for(child : files)
1430          {
1431             // TODO: Platform specific options
1432             if(child.type != resources && (child.type == folder || !child.isExcluded))
1433                child.GenMakefilePrintSymbolRules(f, project);
1434          }
1435       }
1436       delete compiler;
1437    }
1438
1439    void GenMakefilePrintCObjectRules(File f, Project project)
1440    {
1441       //ProjectNode child;
1442       //char objDir[MAX_LOCATION];
1443       CompilerConfig compiler = GetCompilerConfig();
1444       //ReplaceSpaces(objDir, project.config.objDir.dir);
1445       //eSystem_Log("Printing C Object Rules\n");
1446       if(type == file)
1447       {
1448          char extension[MAX_EXTENSION];
1449          char modulePath[MAX_LOCATION];
1450          char moduleName[MAX_FILENAME];
1451          
1452          GetExtension(name, extension);
1453          if(!strcmpi(extension, "ec"))
1454          {
1455             DualPipe dep;
1456             char command[2048];
1457
1458             ReplaceSpaces(moduleName, name);
1459             StripExtension(moduleName);
1460
1461             ReplaceSpaces(modulePath, path);
1462             if(modulePath[0]) strcat(modulePath, SEPS);
1463
1464 #if 0
1465             // *** Dependency command ***
1466             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s",
1467                moduleName, modulePath, moduleName, extension);
1468
1469             // System Includes (from global settings)
1470             for(item : compiler.dirs[Includes])
1471             {
1472                strcat(command, " -isystem ");
1473                if(strchr(item, ' '))
1474                {
1475                   strcat(command, "\"");
1476                   strcat(command, item);
1477                   strcat(command, "\"");
1478                }
1479                else
1480                   strcat(command, item);
1481             }
1482
1483             for(item : project.config.includeDirs)
1484             {
1485                strcat(command, " -I");
1486                if(strchr(item, ' '))
1487                {
1488                   strcat(command, "\"");
1489                   strcat(command, item);
1490                   strcat(command, "\"");
1491                }
1492                else
1493                   strcat(command, item);
1494             }
1495             for(item : project.config.preprocessorDefs)
1496             {
1497                strcat(command, " -D");
1498                strcat(command, item);
1499             }
1500
1501             // Execute it
1502             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1503             {
1504                char line[1024];
1505                bool result = true;
1506                bool firstLine = true;
1507
1508                // To do some time: auto save external dependencies?
1509                while(!dep.Eof())
1510                {
1511                   if(dep.GetLine(line, sizeof(line)-1))
1512                   {
1513                      if(firstLine)
1514                      {
1515                         char * colon = strstr(line, ":");
1516                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1517                         {
1518                            result = false;
1519                            break;
1520                         }
1521                         firstLine = false;
1522                      }
1523                      f.Puts(line);
1524                      f.Puts("\n");
1525                   }
1526                   if(!result) break;
1527                }
1528                delete dep;
1529
1530                // If we failed to generate dependencies...
1531                if(!result)
1532                {
1533                   /* COMMENTED OUT FOR NOW
1534                   f.Printf("$(OBJ)%s.c: %s%s.%s $(Symbols)\n",
1535                      moduleName, modulePath, moduleName, extension);
1536                   */
1537 #endif
1538                   f.Printf("$(OBJ)%s.c: %s%s.%s $(OBJ)%s.sym | $(SYMBOLS)\n",
1539                      moduleName, modulePath, moduleName, extension, moduleName);
1540 #if 0
1541                }
1542             }
1543 #endif
1544          /*
1545             f.Printf("\t$(ECC) %s%s.%s $(OBJ)%s.c\n\n",
1546                modulePath, moduleName, extension, moduleName);
1547          */
1548
1549             f.Printf("\t$(ECC)");
1550             if(ecflags)
1551             {
1552                f.Printf("%s $(CECFLAGS)", noLineNumbers ? " -nolinenumbers" : "");
1553                if(memoryGuard)
1554                   f.Printf(" -memguard");
1555                if(strictNameSpaces)
1556                   f.Printf(" -strictns");
1557                {
1558                   char * s = defaultNameSpace;
1559                   if(s && s[0])
1560                      f.Printf(" -defaultns %s", s);
1561                }
1562             }
1563             else
1564                f.Printf(" $(CECFLAGS) $(ECFLAGS)");
1565             f.Printf(" $(CFLAGS) $(FVISIBILITY)");
1566             GenFileFlags(f, project);
1567             f.Printf(" -c %s%s.%s -o $(OBJ)%s.c -symbols $(OBJ)\n\n",
1568                modulePath, moduleName, extension, moduleName);
1569          }
1570       }
1571       if(files)
1572       {
1573          for(child : files)
1574          {
1575             // TODO: Platform specific options
1576             if(child.type != resources && (child.type == folder || !child.isExcluded))
1577                child.GenMakefilePrintCObjectRules(f, project);
1578          }
1579       }
1580       delete compiler;
1581    }
1582
1583    void GenMakefilePrintObjectRules(File f, Project project, Map<String, NameCollisionInfo> namesInfo)
1584    {
1585       //ProjectNode child;
1586       //char objDir[MAX_LOCATION];
1587       CompilerConfig compiler = GetCompilerConfig();
1588       //ReplaceSpaces(objDir, project.config.objDir.dir);
1589       //eSystem_Log("Printing Object Rules\n");
1590       if(type == file)
1591       {
1592          bool collision;
1593          char extension[MAX_EXTENSION];
1594          char modulePath[MAX_LOCATION];
1595          char moduleName[MAX_FILENAME];
1596          
1597          GetExtension(name, extension);
1598          /*if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1599                !strcmpi(extension, "ec") || !strcmpi(extension, "cc") ||
1600                !strcmpi(extension, "cxx"))*/
1601          if((!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1602                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx")) ||
1603                !strcmpi(extension, "ec"))
1604          {
1605             DualPipe dep;
1606             char command[2048];
1607             NameCollisionInfo info;
1608
1609             ReplaceSpaces(moduleName, name);
1610             StripExtension(moduleName);
1611
1612             info = namesInfo[moduleName];
1613             collision = info ? info.IsExtensionColliding(extension) : false;
1614             
1615             ReplaceSpaces(modulePath, path);
1616             if(modulePath[0]) strcat(modulePath, SEPS);
1617
1618             // *** Dependency command ***
1619             if(!strcmpi(extension, "ec"))
1620                sprintf(command, "%s -MT $(OBJ)%s.o -MM $(OBJ)%s.c", compiler.ccCommand, moduleName, moduleName);
1621             else
1622                sprintf(command, "%s -MT $(OBJ)%s.o -MM %s%s.%s", compiler.ccCommand, moduleName, modulePath, moduleName, extension);
1623
1624             if(!strcmpi(extension, "ec"))
1625                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1626             else
1627             {
1628 #if 0
1629                // System Includes (from global settings)
1630                for(item : compiler.dirs[includes])
1631                {
1632                   strcat(command, " -isystem ");
1633                   if(strchr(item, ' '))
1634                   {
1635                      strcat(command, "\"");
1636                      strcat(command, item);
1637                      strcat(command, "\"");
1638                   }
1639                   else
1640                      strcat(command, item);
1641                }
1642
1643                for(item : project.config.includeDirs)
1644                {
1645                   strcat(command, " -I");
1646                   if(strchr(item, ' '))
1647                   {
1648                      strcat(command, "\"");
1649                      strcat(command, item);
1650                      strcat(command, "\"");
1651                   }
1652                   else
1653                      strcat(command, item);
1654                }
1655                for(item : project.config.preprocessorDefs)
1656                {
1657                   strcat(command, " -D");
1658                   strcat(command, item);
1659                }
1660
1661                // Execute it
1662                if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1663                {
1664                   char line[1024];
1665                   bool firstLine = true;
1666                   bool result = true;
1667
1668                   // To do some time: auto save external dependencies?
1669
1670                   while(!dep.Eof())
1671                   {
1672                      if(dep.GetLine(line, sizeof(line)-1))
1673                      {
1674                         if(firstLine)
1675                         {
1676                            char * colon = strstr(line, ":");
1677                            if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1678                            {
1679                               result = false;
1680                               break;
1681                            }
1682                            firstLine = false;
1683                         }
1684                         f.Puts(line);
1685                         f.Puts("\n");
1686                      }
1687                      if(!result) break;
1688                   }
1689                   delete dep;
1690
1691                   // If we failed to generate dependencies...
1692                   if(!result)
1693                   {
1694 #endif
1695                      /*if(!strcmpi(extension, "ec"))
1696                         f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1697                      else*/
1698                         f.Printf("$(OBJ)%s%s%s.o: %s%s.%s\n", moduleName, 
1699                               collision ? "." : "", collision ? extension : "", modulePath, moduleName, extension);
1700 #if 0
1701                   }
1702                }
1703 #endif
1704             }
1705             f.Printf("\t$(CC) $(CFLAGS)");
1706             GenFileFlags(f, project);
1707             if(!strcmpi(extension, "ec"))
1708                f.Printf(" $(FVISIBILITY) -c $(OBJ)%s.c -o $(OBJ)%s.o\n\n", moduleName, moduleName);
1709             else
1710                f.Printf(" -c %s%s.%s -o $(OBJ)%s%s%s.o\n\n",
1711                      modulePath, moduleName, !strcmpi(extension, "ec") ? "c" : extension, moduleName,
1712                      collision ? "." : "", collision ? extension : "");
1713          }
1714       }
1715       if(files)
1716       {
1717          for(child : files)
1718          {
1719             // TODO: Platform specific options
1720             if(child.type != resources && (child.type == folder || !child.isExcluded))
1721                child.GenMakefilePrintObjectRules(f, project, namesInfo);
1722          }
1723       }
1724       delete compiler;
1725    }
1726
1727    void GenMakefileAddResources(File f, String resourcesPath)
1728    {
1729       int count = 0;
1730       if(files)
1731       {
1732          int c;
1733          bool prev = false;
1734          //Iterator<ProjectNode> i { files };
1735          //Iterator<ProjectNode> prev { files };
1736          //for(child : files)
1737          //while(i.Next())
1738          for(c = 0; c < files.count; c++)
1739          {
1740             ProjectNode child = files[c];
1741             TwoStrings ts = child.platformSpecificFu;
1742             if(count > 0 && ts)
1743                prev = true;
1744             if(child.type == file && !child.isExcluded && !(count > 0 && ts))
1745             {
1746                bool useRes;
1747                char tempPath[MAX_LOCATION];
1748                char resPath[MAX_LOCATION];
1749
1750                char * quotes;
1751
1752                // $(EAR) aw%s --- /*quiet ? "q" : */""
1753                if(count == 0)
1754                   f.Printf("\t%s$(EAR) aw $(TARGET)", ts.a);
1755
1756                tempPath[0] = '\0';
1757                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
1758                {
1759                   useRes = true;
1760                   PathCatSlash(tempPath, child.name);
1761                }
1762                else
1763                {
1764                   useRes = false;
1765                   strcpy(tempPath, child.path);
1766                   PathCatSlash(tempPath, child.name);
1767                }
1768                ReplaceSpaces(resPath, tempPath);
1769                if(strchr(tempPath, ' '))
1770                   quotes = "\"";
1771                else
1772                   quotes = "";
1773                f.Printf(" %s%s%s%s", quotes, useRes ? "$(RES)" : "", resPath, quotes);
1774                count++;
1775             }
1776             if(count == 10 || (count > 0 && (ts || !child.next)))
1777             {
1778                char path[MAX_LOCATION] = "", temp[MAX_LOCATION];
1779                ProjectNode parent;
1780
1781                for(parent = this; parent.type == folder; parent = parent.parent)
1782                {
1783                   strcpy(temp, path);
1784                   strcpy(path, parent.name);
1785                   if(temp[0])
1786                   {
1787                      strcat(path, "/");
1788                      strcat(path, temp);
1789                   }
1790                }
1791                f.Printf(" \"%s\"%s\n", path, ts.b);
1792                count = 0;
1793                if(prev)
1794                {
1795                   c--;
1796                   prev = false;
1797                }
1798             }
1799             delete ts;
1800          }
1801          for(child : files)
1802          {
1803             if(child.type == folder)
1804                child.GenMakefileAddResources(f, resourcesPath);
1805          }
1806       }
1807    }
1808 }
1809
1810 class NameCollisionInfo
1811 {
1812    bool ec;
1813    bool c;
1814    bool cpp;
1815    bool cc;
1816    bool cxx;
1817    bool m;
1818    byte count;
1819
1820    bool IsExtensionColliding(char * extension)
1821    {
1822       bool colliding;
1823       if(count > 1 && ((!strcmpi(extension, "c") && ec) ||
1824             (!strcmpi(extension, "cpp") && (ec || c)) ||
1825             (!strcmpi(extension, "cc") && (ec || c || cpp)) ||
1826             (!strcmpi(extension, "cxx") && (ec || c || cpp || cc)) ||
1827             !strcmpi(extension, "m")))
1828          colliding = true;
1829       else
1830          colliding = false;
1831      return colliding;
1832    }
1833 }