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