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