ide:ProjectView: fixed getting a node by window. this should resolve (#737).
[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) { delete project.options; project.options = value; } else { delete options; 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)
294          {
295             if(project.configurations)
296             {
297                project.configurations.Free();
298                delete project.configurations;
299             }
300             project.configurations = value;
301          }
302          else
303          {
304             if(configurations) { configurations.Free(); delete configurations; }
305             if(value)
306             {
307                List<ProjectConfig> empty { };
308                Iterator<ProjectConfig> it { value };
309                configurations = value;
310                for(c : configurations)
311                {
312                   bool somethingSet = c.options && !c.options.isEmpty;
313                   // TODO: Implement isset keyword
314                   if(!somethingSet && c.platforms && c.platforms.count)
315                   {
316                      for(p : c.platforms)
317                      {
318                         if(p.options && !p.options.isEmpty)
319                         {
320                            somethingSet = true;
321                            break;
322                         }
323                      }
324                   }
325                   if(!somethingSet)
326                      empty.Add(c);
327                }
328                for(c : empty; it.Find(c)) configurations.Delete(it.pointer);
329                delete empty;
330             }
331          }
332       }
333       isset
334       {
335          if(!parent) return true;
336          if(configurations)
337          {
338             for(c : configurations)
339             {
340                bool somethingSet = c.options && !c.options.isEmpty;
341                if(!somethingSet && c.platforms && c.platforms.count)
342                {
343                   for(p : c.platforms)
344                   {
345                      if(p.options && !p.options.isEmpty)
346                      {
347                         somethingSet = true;
348                         break;
349                      }
350                   }
351                }
352                return somethingSet;
353             }
354          }
355          return false;
356       }
357    }
358
359 private:
360    ProjectOptions options;
361    Array<PlatformOptions> platforms;
362    List<ProjectConfig> configurations;
363    ProjectNodeType nodeType;
364    ProjectNode parent;
365    char * name;
366    char * info;
367
368    // This holds the absolute path of the .epj for the project topnode (without the filename)
369    // It holds a relative path to the topNode (project) for other nodes (folders and files)
370    // For folders, it includes the folder it refers to. If there is a name difference between the
371    // file system folder and the grouping folder of the project view, it maps to that folder.
372    char * path;
373    
374    NodeTypes type;
375    NodeIcons icon;
376    int indent;
377    DataRow row;
378
379    bool modified;
380    
381    // This is only set for Top Nodes
382    Project project;
383
384    ProjectConfig GetMatchingNodeConfig(ProjectConfig prjConfig)
385    {
386       ProjectConfig nodeConfig = null;
387       if(property::configurations && prjConfig)
388       {
389          const char * configName = prjConfig.name;
390          for(cfg : property::configurations)
391          {
392             if(!strcmpi(cfg.name, configName))
393             {
394                nodeConfig = cfg;
395                break;
396             }
397          }
398       }
399       return nodeConfig;
400    }
401
402    // For makefile generation:
403    bool GetECFLAGS(ProjectConfig prjConfig)
404    {
405       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
406       ProjectOptions options = property::options;
407       SetBool memoryGuard = localMemoryGuard;
408       String defaultNameSpace = localDefaultNameSpace;
409       SetBool strictNameSpaces = localStrictNameSpaces;
410       SetBool noLineNumbers = localNoLineNumbers;
411
412       if(memoryGuard || defaultNameSpace || strictNameSpaces || noLineNumbers)
413          return true;
414       else if(parent.parent)
415          return parent.GetECFLAGS(prjConfig);
416       else
417          return false;
418    }
419    
420    bool GetMemoryGuard(ProjectConfig prjConfig)
421    {
422       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
423       ProjectOptions options = property::options;
424       SetBool memoryGuard = localMemoryGuard;
425       if(!memoryGuard)
426       {
427          if(parent)
428             return parent.GetMemoryGuard(prjConfig);
429       }
430       return memoryGuard == true;
431    }
432
433    String GetDefaultNameSpace(ProjectConfig prjConfig)
434    {
435       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
436       ProjectOptions options = property::options;
437       String defaultNameSpace = localDefaultNameSpace;
438       if(!defaultNameSpace)
439       {
440          if(parent)
441             return parent.GetDefaultNameSpace(prjConfig);
442       }
443       return defaultNameSpace;
444    }
445
446    bool GetStrictNameSpaces(ProjectConfig prjConfig)
447    {
448       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
449       ProjectOptions options = property::options;
450       SetBool strictNameSpaces = localStrictNameSpaces;
451       if(!strictNameSpaces)
452       {
453          if(parent)
454             return parent.GetStrictNameSpaces(prjConfig);
455       }
456       return strictNameSpaces == true;
457    }
458
459    bool GetNoLineNumbers(ProjectConfig prjConfig)
460    {
461       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
462       ProjectOptions options = property::options;
463       SetBool noLineNumbers = localNoLineNumbers;
464       if(!noLineNumbers)
465       {
466          if(parent)
467             return parent.GetNoLineNumbers(prjConfig);
468       }
469       return noLineNumbers == true;
470    }
471
472    property ProjectNode root { get { ProjectNode n; for(n = this; n.parent; n = n.parent); return n; } }
473
474    property bool containsFile
475    {
476       get
477       {
478          bool result;
479          if(files)
480          {
481             for(child : files)
482             {
483                if(child.type == file ||
484                      ((child.type == folder || child.type == folderOpen) && child.containsFile))
485                {
486                   result = true;
487                   break;
488                }
489             }
490          }
491          else
492             result = false;
493          return result;
494       }
495    }
496
497    char * GetFullFilePath(char * buffer)
498    {
499       if(buffer)
500       {
501          strcpy(buffer, root.path);
502          PathCatSlash(buffer, path);
503          PathCatSlash(buffer, name);
504       }
505       return buffer;
506    }
507
508    char * GetFileSysMatchingPath(char * buffer)
509    {
510       if(buffer)
511       {
512          ProjectNode n, root = this.root;
513          for(n = this; n && (n.type == folder || n.type == project); n = n.parent)
514          {
515             strcpy(buffer, root.path);
516             if(n != root)
517                PathCatSlash(buffer, n.path);
518             if(FileExists(buffer).isDirectory)
519                break;
520          }
521          if(!(n && (n.type == folder || n.type == project)))
522             buffer[0] = '\0';
523       }
524       return buffer;
525    }
526
527    void CollectPerFileAndDirOptions(ProjectConfig prjConfig, Array<String> perFilePreprocessorDefs, Array<DirPath> perFileIncludeDirs)
528    {
529       ProjectNode node = null;
530       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
531       List<ProjectNode> nodeStack { };
532       
533       for(node = this; node && node.parent; node = node.parent)
534          nodeStack.Add(node);
535
536       // Should we reverse this stack to give priority to the per-file includes? Does the following technique already reverse? 
537
538       // TODO: Check how to fix duplication of following options when configuration is made per-config-per-file
539       while((node = nodeStack.lastIterator.data))
540       {
541          ProjectConfig config = GetMatchingNodeConfig(prjConfig);
542          ProjectOptions nodeOptions = node.property::options;
543          if(nodeOptions && nodeOptions.preprocessorDefinitions)
544          {
545             for(def : nodeOptions.preprocessorDefinitions)
546                perFilePreprocessorDefs.Add(CopyString(def));
547          }
548          if(config && config.options && config.options.preprocessorDefinitions)
549          {
550             for(def : config.options.preprocessorDefinitions)
551                perFilePreprocessorDefs.Add(CopyString(def));
552          }
553          if(nodeOptions && nodeOptions.includeDirs)
554          {
555             for(dir : nodeOptions.includeDirs)
556                perFileIncludeDirs.Add(CopySystemPath(dir));
557          }
558          if(config && config.options && config.options.includeDirs)
559          {
560             for(dir : config.options.includeDirs)
561                perFileIncludeDirs.Add(CopySystemPath(dir));
562          }
563          nodeStack.lastIterator.Remove();
564       }
565       delete nodeStack;
566    }
567
568
569    property Project project
570    {
571       get
572       {
573          ProjectNode n = this;
574          while(n && n.type != project) n = n.parent;
575          return n ? (*&n.project) : null;
576       }
577    }   
578
579    void RenameConfig(char * oldName, char * newName)
580    {
581       if(files)
582       {
583          for(f : files; (f.configurations || f.files)) { f.RenameConfig(oldName, newName); }
584       }
585       if(property::configurations)
586       {
587          for(c : property::configurations; !strcmp(c.name, oldName))
588          {
589             delete c.name;
590             c.name = CopyString(newName);
591          }
592       }
593    }
594
595    void DeleteConfig(ProjectConfig configToDelete)
596    {
597       if(files)
598       {
599          for(f : files; (f.configurations || f.files)) { f.DeleteConfig(configToDelete); }
600       }
601       if(property::configurations)
602       {
603          Iterator<ProjectConfig> c { property::configurations };
604          while(c.Next())
605          {
606             ProjectConfig config = c.data;
607             if(!strcmp(configToDelete.name, config.name))
608             {               
609                c.Remove();
610                delete config;
611                break;
612             }
613          }
614          if(!property::configurations.count)
615             property::configurations = null;
616       }
617    }
618
619    ProjectNode Backup()
620    {
621       ProjectNode backupNode { };
622
623       if(files)
624       {
625          backupNode.files = { };
626          for(f : files) backupNode.files.Add(f.Backup());
627       }
628       if(property::options)
629          backupNode.options = property::options.Copy();
630
631       if(property::platforms)
632       {
633          backupNode.platforms = { };
634          for(p : property::platforms)
635             backupNode.platforms.Add(p.Copy());
636       }
637
638       if(property::configurations)
639       {
640          backupNode.configurations = { };
641          for(c : property::configurations)
642             backupNode.configurations.Add(c.Copy());
643       }
644       return backupNode;
645    }
646
647    void Revert(ProjectNode backupNode)
648    {
649       if(files)
650       {
651          Iterator<ProjectNode> it { backupNode.files };
652          for(f : files)
653          {
654             it.Next();
655             f.Revert(it.data);
656          }
657       }
658
659       property::options = backupNode.options ? backupNode.options.Copy() : null;
660       if(backupNode.platforms)
661       {
662          Array<PlatformOptions> platforms { };
663          property::platforms = platforms;
664
665          for(p : backupNode.platforms)
666             platforms.Add(p.Copy());
667       }
668       if(backupNode.configurations)
669       {
670          List<ProjectConfig> configurations { };
671          property::configurations = configurations;
672          for(c : backupNode.configurations)
673             configurations.Add(c.Copy());
674       }
675    }
676
677    void FixupNode(char * parentPath)
678    {
679       if(!parent)
680       {
681          type = project;
682       }
683       else if(nodeType == file)
684       {
685          type = file;
686          if(!path)
687          {
688             path = CopyString((parent.type == folder || parent.type == resources) ? parentPath : "");
689          }
690       }
691       else if(nodeType == folder)
692       {
693          type = folder;
694
695          if(!path)
696          {
697             char temp[MAX_LOCATION];
698             strcpy(temp, (parent.type == folder || parent.type == resources) ? parentPath : "");
699             PathCatSlash(temp, name);
700             path = CopyString(temp);
701          }
702       }
703
704       indent = parent ? parent.indent + 1 : 0;
705
706       if(type == file)
707          icon = NodeIcons::SelectFileIcon(name);
708       else
709          icon = NodeIcons::SelectNodeIcon(type);
710
711       if(files)
712       {
713          for(f : files)
714          {
715             f.parent = this;
716
717             if(type == project)
718                parentPath[0] = '\0';
719             else if(type == resources || type == folder)
720                strcpy(parentPath, path);
721
722             f.FixupNode(parentPath);
723          }
724       }
725    }
726
727    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
728    {
729       if(!needClass)
730       {
731          // TOCHECK: Called from JSON writer
732          if(nodeType == file && !property::options && !property::configurations && !property::platforms && name)
733          {
734             strcpy(tempString, "\"");
735             strcat(tempString, property::fileName);
736             strcat(tempString, "\"");
737             return tempString;
738          }
739          else
740             return null;
741       }
742       else
743          // TOCHECK: Called from ProjectView rendering
744          return name ? name : "";
745    }
746
747    ~ProjectNode()
748    {
749       if(files)
750       {
751          files.Free();
752          delete files;
753       }
754       if(!project)
755          delete options;
756
757       if(!project && platforms)
758       {
759          platforms.Free();
760          delete platforms;
761       };
762       if(!project && configurations)
763       {
764          configurations.Free();
765          delete configurations;
766       }
767
768       /////////////////////////////
769       delete path;
770       delete name;
771       delete info;
772    }
773
774    property bool isInResources
775    {
776       get
777       {
778          ProjectNode node;
779          for(node = this; node; node = node.parent)
780          {
781             if(node.type == resources)
782                return true;
783          }
784          return false;
785       }
786    }
787
788    TwoStrings GetPlatformSpecificFu(ProjectConfig prjConfig)
789    {
790       TwoStrings result { a = CopyString(""), b = CopyString("") };
791       // note: unknown platform is for common
792       Map<Platform, SetBool> exclusionInfo { };
793       MapNode<Platform, SetBool> mn;
794       char * exp, * var;
795       int len;
796       SetBool common;
797
798       CollectExclusionInfo(exclusionInfo, prjConfig);
799       common = exclusionInfo[unknown];
800       {
801          Map<Platform, SetBool> cleaned { };
802          SetBool opposite = common == true ? false : true;
803          for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
804          {
805             if(mn.key == unknown || mn.value == opposite)
806               cleaned[mn.key] = mn.value;
807          }
808          delete exclusionInfo;
809          exclusionInfo = cleaned;
810       }
811
812       if(exclusionInfo.count > 1)
813       {
814          if(exclusionInfo.count > 2)
815          {
816             exp = result.a;
817             len = strlen(exp) + strlen("$(if $(or ");
818             exp = renew exp char[len+1];
819             strcat(exp, "$(if $(or ");
820             result.a = exp;
821
822             for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
823             {
824                if(mn.key != unknown)
825                {
826                   char * comma = mn.next ? "," : "";
827
828                   var = PlatformToMakefileVariable(mn.key);
829
830                   exp = result.a;
831                   len = strlen(exp) + strlen("$(") + strlen(var) + strlen(")") + strlen(comma);
832                   exp = renew exp char[len+1];
833                   strcat(exp, "$(");
834                   strcat(exp, var);
835                   strcat(exp, ")");
836                   strcat(exp, comma);
837                   result.a = exp;
838                }
839             }
840
841             exp = result.a;
842             len = strlen(exp) + strlen("),");
843             exp = renew exp char[len+1];
844          }
845          else
846          {
847             if(exclusionInfo.root.minimum.key != unknown)
848                var = PlatformToMakefileVariable(exclusionInfo.root.minimum.key);
849             else
850                var = PlatformToMakefileVariable(exclusionInfo.root.minimum.next.key);
851
852             exp = result.a;
853             len = strlen(exp) + strlen("$(if $(") + strlen(var) + strlen("),");
854             exp = renew exp char[len+1];
855             strcat(exp, "$(if $(");
856             strcat(exp, var);
857          }
858
859          strcat(exp, "),");
860          result.a = exp;
861
862          exp = common == true ? result.b : result.a;
863          len = strlen(exp) + strlen(",");
864          exp = renew exp char[len+1];
865          strcat(exp, ",");
866          if(common == true) result.b = exp; else result.a = exp;
867
868          exp = result.b;
869          len = strlen(exp) + strlen(")");
870          exp = renew exp char[len+1];
871          strcat(exp, ")");
872          result.b = exp;
873       }
874       delete exclusionInfo;
875       
876       return result;
877    }
878
879    bool GetIsExcluded(ProjectConfig prjConfig)
880    {
881       bool result;
882       // note: unknown platform is for common
883       Map<Platform, SetBool> exclusionInfo { };
884       CollectExclusionInfo(exclusionInfo, prjConfig);
885       if(exclusionInfo.count == 0)
886          result = false;
887       else if(exclusionInfo.count == 1)
888          result = exclusionInfo.root.minimum.value == true;
889       else
890       {
891          SetBool check = exclusionInfo.root.minimum.value;
892          MapNode<Platform, SetBool> mn;
893          for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
894          {
895             if(check != mn.value)
896                break;
897          }
898          if(!mn) // all are same
899             result = check == true;
900          else
901             result = false;
902       }
903       delete exclusionInfo;
904       return result;
905    }
906
907    void CollectExclusionInfo(Map<Platform, SetBool> output, ProjectConfig prjConfig)
908    {
909       // note: unknown platform is for common
910       Platform platform;
911       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
912       ProjectOptions options = property::options;
913       Array<PlatformOptions> platforms = property::platforms;
914       List<ProjectConfig> configurations = property::configurations;
915
916       if(parent)
917          parent.CollectExclusionInfo(output, prjConfig);
918       else
919          output[unknown] = unset;
920
921       if(options && options.excludeFromBuild)
922          output[unknown] = options.excludeFromBuild;
923       
924       if(config && config.options && config.options.excludeFromBuild)
925          output[unknown] = config.options.excludeFromBuild;
926
927       if(platforms)
928       {
929          for(p : platforms)
930          {
931             if(p.options.excludeFromBuild && (platform = p.name))
932                output[platform] = p.options.excludeFromBuild;
933          }
934       }
935       if(config && config.platforms)
936       {
937          for(p : config.platforms)
938          {
939             if(p.options.excludeFromBuild && (platform = p.name))
940                output[platform] = p.options.excludeFromBuild;
941          }
942       }
943    }
944
945    void EnsureVisible()
946    {
947       if(parent)
948          parent.EnsureVisible();
949       row.collapsed = false;
950    }
951
952    void Delete()
953    {
954       if(parent)
955          parent.files.Delete(this);
956    }
957
958    ProjectNode Find(char * name, bool includeResources)
959    {
960       ProjectNode result = null;
961       if(files)
962       {
963          for(child : files)
964          {
965             if(includeResources || child.type != resources)
966             {
967                if(child.type != folder && child.name && !strcmpi(child.name, name))
968                {
969                   result = child;
970                   break;
971                }
972                result = child.Find(name, includeResources);
973                if(result)
974                   break;
975             }
976          }
977       }
978       return result;
979    }
980
981    ProjectNode FindWithPath(char * name, bool includeResources)
982    {
983       ProjectNode result = null;
984       if(files)
985       {
986          for(child : files)
987          {
988             if(includeResources || child.type != resources)
989             {
990                char path[MAX_LOCATION];
991                strcpy(path, child.path);
992                if(child.type != folder && child.name)
993                {
994                   PathCatSlash(path, child.name);
995                   if(!strcmpi(path, name))
996                   {
997                      result = child;
998                      break;
999                   }
1000                }
1001                result = child.FindWithPath(name, includeResources);
1002                if(result)
1003                   break;
1004             }
1005          }
1006       }
1007       return result;
1008    }
1009
1010    ProjectNode FindByFullPath(char * path, bool includeResources)
1011    {
1012       ProjectNode result = null;
1013       if(files)
1014       {
1015          for(child : files)
1016          {
1017             if(includeResources || child.type != resources)
1018             {
1019                if(child.type != folder && child.name)
1020                {
1021                   char p[MAX_LOCATION];
1022                   child.GetFullFilePath(p);
1023                   if(!strcmpi(p, path))
1024                   {
1025                      result = child;
1026                      break;
1027                   }
1028                }
1029                result = child.FindByFullPath(path, includeResources);
1030                if(result)
1031                   break;
1032             }
1033          }
1034       }
1035       return result;
1036    }
1037
1038    ProjectNode FindSpecial(char * name, bool recursive, bool includeResources, bool includeFolders)
1039    {
1040       ProjectNode result = null;
1041       if(files)
1042       {
1043          for(child : files)
1044          {
1045             if(includeResources || child.type != resources)
1046             {
1047                if((includeFolders || child.type != folder) && child.name && !strcmpi(child.name, name))
1048                {
1049                   result = child;
1050                   break;
1051                }
1052                if(recursive)
1053                   result = child.FindSpecial(name, recursive, includeResources, includeFolders);
1054                if(result)
1055                   break;
1056             }
1057          }
1058       }
1059       return result;
1060    }
1061
1062    ProjectNode FindSameNameConflict(char * name, bool includeResources,
1063       Map<Platform, SetBool> exclusionInfo, ProjectConfig prjConfig)
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, prjConfig);
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, prjConfig);
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       
1129       // TOCHECK: Shouldn't this apply either for all configs or none?
1130       CollectExclusionInfo(exclusionInfo, project.config);
1131       if(!checkIfExists || !project.topNode.FindSameNameConflict(temp, false, exclusionInfo, project.config))
1132       {
1133          // Do the check for folder in the same parent or resource files only here
1134          if(type == folder || !checkIfExists)
1135          {
1136             for(node : files)
1137             {
1138                if(node.name && !strcmpi(node.name, temp))
1139                   return null;
1140             }
1141          }
1142
1143          node = ProjectNode { parent = this, indent = indent + 1, type = type, icon = icon, name = CopyString(temp) };
1144          if(type != file)
1145          {
1146             node.files = { }; 
1147             node.nodeType = folder;
1148          }
1149          if(type != folder)
1150          {
1151             if(filePath)
1152             {
1153                StripLastDirectory(filePath, temp);
1154                MakePathRelative(temp, project.topNode.path, temp);
1155                node.path = CopyUnixPath(temp);
1156             }
1157             node.nodeType = file;
1158          }
1159          else
1160          {
1161             strcpy(temp, (type == NodeTypes::project) ? "" : path);
1162             PathCatSlash(temp, node.name);
1163             node.path = CopyString(temp);
1164          }
1165          files.Insert(after, node);
1166       }
1167       delete exclusionInfo;
1168       return node;
1169    }
1170
1171 #ifndef MAKEFILE_GENERATOR
1172    void OnDisplay(Surface surface, int x, int y, int width, ProjectView projectView, Alignment alignment, DataDisplayFlags displayFlags)
1173    {
1174       char label[MAX_FILENAME];
1175       int indent = 16;
1176       int xStart;
1177       int len;
1178       int w, h;
1179       Bitmap bmp;
1180       bool showConfig = true;
1181
1182       if(!projectView)
1183       {
1184          showConfig = false;
1185          projectView = ide.projectView;
1186       }         
1187       
1188       bmp = projectView.icons[icon].bitmap;
1189       xStart = /*indent * indent + */x + (bmp ? (bmp.width + 5) : 0);
1190
1191       GetLastDirectory(name, label);
1192       if(!showConfig || projectView.drawingInProjectSettingsDialogHeader)
1193       {
1194          if(projectView.drawingInProjectSettingsDialogHeader || (type == project && info))
1195          {
1196             if(projectView.projectSettingsDialog && projectView.projectSettingsDialog.buildTab)
1197             {
1198                char * addendum;
1199                addendum = projectView.projectSettingsDialog.buildTab.selectedConfigName;
1200                if(strlen(addendum))
1201                {
1202                   strcat(label, " (");
1203                   strcat(label, addendum);
1204                   strcat(label, ")");
1205                }
1206                addendum = projectView.projectSettingsDialog.buildTab.selectedPlatformName;
1207                if(strlen(addendum))
1208                {
1209                   strcat(label, " (");
1210                   strcat(label, addendum);
1211                   strcat(label, ")");
1212                }
1213             }
1214          }
1215       }
1216       else if(!projectView.drawingInProjectSettingsDialog)
1217       {
1218          if(modified)
1219             strcat(label, " *");
1220          if(type == project && info)
1221          {
1222             int len = strlen(info) + 4;
1223             char * more = new char[len];
1224             sprintf(more, " (%s)", info);
1225             strcat(label, more);
1226             delete more;
1227          }
1228       }
1229       len = strlen(label);
1230       
1231       if(!bmp)
1232       {
1233          if(type == folder || type == folderOpen)
1234             surface.SetForeground(yellow);
1235          indent = 8;
1236       }
1237
1238       surface.TextOpacity(false);
1239       surface.TextExtent(label, len, &w, &h);
1240       h = Max(h, 16);
1241     
1242       // Draw the current row stipple
1243       if(displayFlags.selected)
1244          //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
1245          //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
1246          surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
1247       
1248       surface.WriteTextDots(alignment, xStart, y + 2, width, label, len);
1249       
1250       if(!app.textMode)
1251       {
1252          if(displayFlags.current)
1253          {
1254             if(displayFlags.active)
1255             {
1256                surface.LineStipple(0x5555);
1257                if(displayFlags.selected)
1258                   surface.SetForeground(projectView.fileList.stippleColor);
1259                else
1260                   surface.SetForeground(projectView.fileList.foreground);
1261             }
1262             else
1263             {
1264                surface.SetForeground(SELECTION_COLOR);
1265             }
1266             surface.Rectangle(xStart - 3, y, xStart + w + 1, y + h - 1);
1267             surface.LineStipple(0);
1268          }
1269
1270          if(bmp)
1271          {
1272             surface.SetForeground(white);
1273             surface.Blit(bmp, x /*+ indent * indent*/,y,0,0, bmp.width, bmp.height);
1274          }
1275       }
1276    }
1277 #endif
1278
1279    int OnCompare(ProjectNode b)
1280    {
1281       int result;
1282       if(type == b.type /*|| type >= TYPE_DRIVE*/)
1283          result = strcmpi(name, b.name);
1284       else
1285       {
1286          if(type == folder && b.type == file) result = -1;
1287          else if(type == file && b.type == folder) result = 1;
1288       }
1289       return result;
1290    }
1291
1292    bool ContainsFilesWithExtension(char * extension)
1293    {
1294       if(type == file)
1295       {
1296          char ext[MAX_EXTENSION];
1297          GetExtension(name, ext);
1298          if(!fstrcmp(ext, extension))
1299             return true;
1300       }
1301       else if(files)
1302       {
1303          bool needed = false;
1304          for(child : files)
1305             if(child.ContainsFilesWithExtension(extension))
1306                return true;
1307       }
1308       return false;
1309    }
1310
1311    void GenFileFlags(File f, Project project, ProjectConfig prjConfig)
1312    {
1313       ProjectNode node = null;
1314       List<ProjectNode> nodeStack { };
1315       
1316       for(node = this; node && node.parent; node = node.parent)
1317          nodeStack.Add(node);
1318
1319       // Should we reverse this stack to give priority to the per-file includes?
1320
1321       while((node = nodeStack.lastIterator.data))
1322       {
1323          ProjectOptions nodeOptions = node.property::options;
1324          ProjectConfig config = node.GetMatchingNodeConfig(prjConfig);
1325          if(nodeOptions && nodeOptions.preprocessorDefinitions)
1326             OutputListOption(f, "D", nodeOptions.preprocessorDefinitions, inPlace, false);
1327          if(config && config.options && config.options.preprocessorDefinitions)
1328             OutputListOption(f, "D", config.options.preprocessorDefinitions, inPlace, false);
1329          if(nodeOptions && nodeOptions.includeDirs)
1330             OutputListOption(f, "I", nodeOptions.includeDirs, inPlace, true);
1331          if(config && config.options && config.options.includeDirs)
1332             OutputListOption(f, "I", config.options.includeDirs, inPlace, true);
1333
1334          nodeStack.lastIterator.Remove();
1335       }
1336       delete nodeStack;
1337    }
1338
1339    void GenMakefileGetNameCollisionInfo(Map<String, NameCollisionInfo> namesInfo, ProjectConfig prjConfig)
1340    {
1341       if(type == file)
1342       {
1343          char extension[MAX_EXTENSION];
1344          GetExtension(name, extension);
1345          if(!strcmpi(extension, "ec") || !strcmpi(extension, "c") ||
1346                !strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
1347                !strcmpi(extension, "cxx") || !strcmpi(extension, "m"))
1348          {
1349             char moduleName[MAX_FILENAME];
1350             NameCollisionInfo info;
1351             ReplaceSpaces(moduleName, name);
1352             StripExtension(moduleName);
1353             info = namesInfo[moduleName];
1354             if(!info)
1355                info = NameCollisionInfo { };
1356             info.count++; // += 1; unless this is for a bug?
1357             if(!strcmpi(extension, "ec"))
1358                info.ec = true;
1359             else if(!strcmpi(extension, "c"))
1360                info.c = true;
1361             else if(!strcmpi(extension, "cpp"))
1362                info.cpp = true;
1363             else if(!strcmpi(extension, "cc"))
1364                info.cc = true;
1365             else if(!strcmpi(extension, "cxx"))
1366                info.cxx = true;
1367             else if(!strcmpi(extension, "m"))
1368                info.m = true;
1369             namesInfo[moduleName] = info;
1370          }
1371       }
1372       else if(files)
1373       {
1374          for(child : files)
1375          {
1376             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1377                child.GenMakefileGetNameCollisionInfo(namesInfo, prjConfig);
1378          }
1379       }
1380    }
1381    
1382    int GenMakefilePrintNode(File f, Project project, GenMakefilePrintTypes printType,
1383       Map<String, NameCollisionInfo> namesInfo, Array<String> items,
1384       ProjectConfig prjConfig)
1385    {
1386       int count = 0;
1387       if(type == file)
1388       {
1389          char s[2048];
1390          TwoStrings ts = GetPlatformSpecificFu(prjConfig);
1391          char moduleName[MAX_FILENAME];
1392          char extension[MAX_EXTENSION];
1393          GetExtension(name, extension);
1394          if(printType == resources)
1395          {
1396             bool useRes;
1397             char tempPath[MAX_LOCATION];
1398             char modulePath[MAX_LOCATION];
1399
1400             tempPath[0] = '\0';
1401             if(eString_PathInsideOfMore(path, project.resNode.path, tempPath))
1402             {
1403                useRes = true;
1404                PathCatSlash(tempPath, name);
1405             }
1406             else
1407             {
1408                useRes = false;
1409                strcpy(tempPath, path);
1410                PathCatSlash(tempPath, name);
1411             }
1412             ReplaceSpaces(modulePath, tempPath);
1413             sprintf(s, "%s%s%s%s", ts.a, useRes ? "$(RES)" : "", modulePath, ts.b);
1414             items.Add(CopyString(s));
1415          }
1416          else if(printType == sources)
1417          {
1418             if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1419                   !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1420                   !strcmpi(extension, "ec") || !strcmpi(extension, "m"))
1421             {
1422                char modulePath[MAX_LOCATION];
1423
1424                ReplaceSpaces(modulePath, path);
1425                ReplaceSpaces(moduleName, name);
1426                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
1427                items.Add(CopyString(s));
1428             }
1429          }
1430          else if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1431                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1432                !strcmpi(extension, "m"))
1433          {
1434             if(printType == objects)
1435             {
1436                bool collision;
1437                NameCollisionInfo info;
1438                ReplaceSpaces(moduleName, name);
1439                StripExtension(moduleName);
1440                info = namesInfo[moduleName];
1441                collision = info ? info.IsExtensionColliding(extension) : false;
1442                sprintf(s, "%s$(OBJ)%s%s%s.o%s", ts.a, moduleName, collision ? "." : "", collision ? extension : "", ts.b);
1443                items.Add(CopyString(s));
1444             }
1445          }
1446          else if(!strcmpi(extension, "ec"))
1447          {
1448             ReplaceSpaces(moduleName, name);
1449             StripExtension(moduleName);
1450             if(printType == objects)
1451                count++;
1452             s[0] = '\0';
1453             if(printType == objects)
1454                sprintf(s, "%s$(OBJ)%s.o%s", ts.a, moduleName, ts.b);
1455             else if(printType == cObjects)
1456                sprintf(s, "%s$(OBJ)%s.c%s", ts.a, moduleName, ts.b);
1457             else if(printType == symbols)
1458                sprintf(s, "%s$(OBJ)%s.sym%s", ts.a, moduleName, ts.b);
1459             else if(printType == imports)
1460                sprintf(s, "%s$(OBJ)%s.imp%s", ts.a, moduleName, ts.b);
1461             if(s[0])
1462                items.Add(CopyString(s));
1463          }
1464          delete ts;
1465       }
1466       else if(files)
1467       {
1468          for(child : files)
1469          {
1470             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1471                count += child.GenMakefilePrintNode(f, project, printType, namesInfo, items, prjConfig);
1472          }
1473       }
1474       return count;
1475    }
1476
1477    void GenMakefilePrintSymbolRules(File f, Project project, CompilerConfig compiler,
1478          ProjectConfig prjConfig, Map<Platform, bool> parentExcludedPlatforms)
1479    {
1480       int ifCount = 0;
1481       Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(prjConfig);
1482       //ProjectNode child;
1483       //char objDir[MAX_LOCATION];
1484       //ReplaceSpaces(objDir, config.objDir.dir);
1485
1486       //eSystem_Log("Printing Symbol Rules\n");
1487       if(type == file)
1488       {
1489          char extension[MAX_EXTENSION];
1490          char modulePath[MAX_LOCATION];
1491          char moduleName[MAX_FILENAME];
1492
1493          GetExtension(name, extension);
1494          if(!strcmpi(extension, "ec"))
1495          {
1496             DualPipe dep;
1497             char command[2048];
1498
1499             ReplaceSpaces(moduleName, name);
1500             StripExtension(moduleName);
1501
1502             ReplaceSpaces(modulePath, path);
1503             if(modulePath[0]) strcat(modulePath, SEPS);
1504
1505 #if 0
1506             // *** Dependency command ***
1507             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s", moduleName,
1508                modulePath, moduleName, extension);
1509
1510             // System Includes (from global settings)
1511             for(item : compiler.dirs[Includes])
1512             {
1513                strcat(command, " -isystem ");
1514                if(strchr(item, ' '))
1515                {
1516                   strcat(command, "\"");
1517                   strcat(command, item);
1518                   strcat(command, "\"");
1519                }
1520                else
1521                   strcat(command, item);
1522             }
1523
1524             for(item : project.includeDirs)
1525             {
1526                strcat(command, " -I");
1527                if(strchr(item, ' '))
1528                {
1529                   strcat(command, "\"");
1530                   strcat(command, item);
1531                   strcat(command, "\"");
1532                }
1533                else
1534                   strcat(command, item);
1535             }
1536             for(item : project.preprocessorDefs)
1537             {
1538                strcat(command, " -D");
1539                strcat(command, item);
1540             }
1541
1542             // Execute it
1543             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1544             {
1545                char line[1024];
1546                bool firstLine = true;
1547                bool result = true;
1548
1549                // To do some time: auto save external dependencies?
1550                while(!dep.Eof())
1551                {
1552                   if(dep.GetLine(line, sizeof(line)-1))
1553                   {
1554                      if(firstLine)
1555                      {
1556                         char * colon = strstr(line, ":");
1557                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1558                         {
1559                            result = false;
1560                            break;
1561                         }
1562                         firstLine = false;
1563                      }
1564                      f.Puts(line);
1565                      f.Puts("\n");
1566                   }
1567                   if(!result) break;
1568                }
1569                delete dep;
1570
1571                // If we failed to generate dependencies...
1572                if(!result)
1573                {
1574 #endif
1575                OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
1576                   f.Printf("$(OBJ)%s.sym: %s%s.%s\n",
1577                      moduleName, modulePath, moduleName, extension);
1578 #if 0
1579                }
1580             }
1581 #endif
1582          /*
1583             f.Printf("\t$(ECP) %s%s.%s %s.sym\n\n",
1584                modulePath, moduleName, extension, moduleName);
1585             */
1586
1587             f.Printf("\t$(ECP)");
1588             // Give priority to file flags
1589             GenFileFlags(f, project, prjConfig);
1590
1591             f.Printf(" $(CECFLAGS)");
1592             if(GetECFLAGS(prjConfig))
1593             {
1594                if(GetMemoryGuard(prjConfig))
1595                   f.Printf(" -memguard");
1596                if(GetStrictNameSpaces(prjConfig))
1597                   f.Printf(" -strictns");
1598                {
1599                   char * s = GetDefaultNameSpace(prjConfig);
1600                   if(s && s[0])
1601                      f.Printf(" -defaultns %s", s);
1602                }
1603             }
1604             else
1605                f.Printf(" $(ECFLAGS)");
1606             f.Printf(" $(CFLAGS)");
1607
1608             f.Printf(" -c %s%s.%s -o $(OBJ)%s.sym\n\n",
1609                modulePath, moduleName, extension, moduleName);
1610             CloseRulesPlatformExclusionIfs(f, ifCount);
1611          }
1612       }
1613       if(files)
1614       {
1615          bool needed = false;
1616          if(ContainsFilesWithExtension("ec"))
1617          {
1618             for(child : files)
1619             {
1620                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1621                {
1622                   needed = true;
1623                   break;
1624                }
1625             }
1626          }
1627          if(needed)
1628          {
1629             Map<Platform, bool> excludedPlatforms { };
1630             for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
1631             for(platform : platforms)
1632             {
1633                OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
1634                for(child : files)
1635                {
1636                   if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1637                      child.GenMakefilePrintSymbolRules(f, project, compiler, prjConfig, excludedPlatforms);
1638                }
1639             }
1640             CloseRulesPlatformExclusionIfs(f, ifCount);
1641             delete excludedPlatforms;
1642          }
1643       }
1644       delete platforms;
1645    }
1646
1647    void GenMakefilePrintPrepecsRules(File f, Project project, CompilerConfig compiler,
1648          ProjectConfig prjConfig, Map<Platform, bool> parentExcludedPlatforms)
1649    {
1650       int ifCount = 0;
1651       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1652       Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(prjConfig);
1653       //ProjectNode child;
1654       //char objDir[MAX_LOCATION];
1655       //ReplaceSpaces(objDir, config.objDir.dir);
1656
1657       //eSystem_Log("Printing Symbol Rules\n");
1658       if(type == file)
1659       {
1660          char extension[MAX_EXTENSION];
1661          char modulePath[MAX_LOCATION];
1662          char moduleName[MAX_FILENAME];
1663
1664          GetExtension(name, extension);
1665          if(!strcmpi(extension, "ec"))
1666          {
1667             DualPipe dep;
1668             char command[2048];
1669
1670             ReplaceSpaces(moduleName, name);
1671             StripExtension(moduleName);
1672
1673             ReplaceSpaces(modulePath, path);
1674             if(modulePath[0]) strcat(modulePath, SEPS);
1675
1676             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
1677             f.Printf("$(OBJ)%s$(EC): %s%s.%s\n",
1678                moduleName, modulePath, moduleName, extension);
1679             //$(CPP) -x c -E ../extras/gui/controls/DirectoriesBox.ec -o $(OBJ)DirectoriesBox$(EC)
1680             /*f.Printf("\t$(CPP) %s%s.%s %s$(S)\n\n",
1681                modulePath, moduleName, extension, moduleName);*/
1682
1683             f.Printf("\t$(CPP)");
1684             // Give priority to file flags
1685             GenFileFlags(f, project, prjConfig);
1686
1687             /*f.Printf(" $(CECFLAGS)");
1688             if(GetECFLAGS(prjConfig))
1689             {
1690                if(GetMemoryGuard(prjConfig))
1691                   f.Printf(" -memguard");
1692                if(GetStrictNameSpaces(prjConfig))
1693                   f.Printf(" -strictns");
1694                {
1695                   char * s = GetDefaultNameSpace(prjConfig);
1696                   if(s && s[0])
1697                      f.Printf(" -defaultns %s", s);
1698                }
1699             }
1700             else
1701                f.Printf(" $(ECFLAGS)");*/
1702             f.Printf(" $(CFLAGS)");
1703
1704             f.Printf(" -x c -E %s%s.%s -o $(OBJ)%s$(EC)\n\n",
1705                modulePath, moduleName, extension, moduleName);
1706             CloseRulesPlatformExclusionIfs(f, ifCount);
1707          }
1708       }
1709       if(files)
1710       {
1711          bool needed = false;
1712          if(ContainsFilesWithExtension("ec"))
1713          {
1714             for(child : files)
1715             {
1716                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1717                {
1718                   needed = true;
1719                   break;
1720                }
1721             }
1722          }
1723          if(needed)
1724          {
1725             Map<Platform, bool> excludedPlatforms { };
1726             for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
1727             for(platform : platforms)
1728             {
1729                OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
1730                for(child : files)
1731                {
1732                   if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1733                      child.GenMakefilePrintPrepecsRules(f, project, compiler, prjConfig, excludedPlatforms);
1734                }
1735             }
1736             CloseRulesPlatformExclusionIfs(f, ifCount);
1737             delete excludedPlatforms;
1738          }
1739       }
1740       delete platforms;
1741    }
1742
1743    void GenMakefilePrintCObjectRules(File f, Project project, CompilerConfig compiler,
1744       ProjectConfig prjConfig, Map<Platform, bool> parentExcludedPlatforms)
1745    {
1746       int ifCount = 0;
1747       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1748       Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(prjConfig);
1749       //ProjectNode child;
1750       //char objDir[MAX_LOCATION];
1751       //ReplaceSpaces(objDir, config.objDir.dir);
1752       //eSystem_Log("Printing C Object Rules\n");
1753       if(type == file)
1754       {
1755          char extension[MAX_EXTENSION];
1756          char modulePath[MAX_LOCATION];
1757          char moduleName[MAX_FILENAME];
1758
1759          GetExtension(name, extension);
1760          if(!strcmpi(extension, "ec"))
1761          {
1762             DualPipe dep;
1763             char command[2048];
1764
1765             ReplaceSpaces(moduleName, name);
1766             StripExtension(moduleName);
1767
1768             ReplaceSpaces(modulePath, path);
1769             if(modulePath[0]) strcat(modulePath, SEPS);
1770
1771 #if 0
1772             // *** Dependency command ***
1773             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s",
1774                moduleName, modulePath, moduleName, extension);
1775
1776             // System Includes (from global settings)
1777             for(item : compiler.dirs[Includes])
1778             {
1779                strcat(command, " -isystem ");
1780                if(strchr(item, ' '))
1781                {
1782                   strcat(command, "\"");
1783                   strcat(command, item);
1784                   strcat(command, "\"");
1785                }
1786                else
1787                   strcat(command, item);
1788             }
1789
1790             for(item : config.includeDirs)
1791             {
1792                strcat(command, " -I");
1793                if(strchr(item, ' '))
1794                {
1795                   strcat(command, "\"");
1796                   strcat(command, item);
1797                   strcat(command, "\"");
1798                }
1799                else
1800                   strcat(command, item);
1801             }
1802             for(item : config.preprocessorDefs)
1803             {
1804                strcat(command, " -D");
1805                strcat(command, item);
1806             }
1807
1808             // Execute it
1809             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1810             {
1811                char line[1024];
1812                bool result = true;
1813                bool firstLine = true;
1814
1815                // To do some time: auto save external dependencies?
1816                while(!dep.Eof())
1817                {
1818                   if(dep.GetLine(line, sizeof(line)-1))
1819                   {
1820                      if(firstLine)
1821                      {
1822                         char * colon = strstr(line, ":");
1823                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1824                         {
1825                            result = false;
1826                            break;
1827                         }
1828                         firstLine = false;
1829                      }
1830                      f.Puts(line);
1831                      f.Puts("\n");
1832                   }
1833                   if(!result) break;
1834                }
1835                delete dep;
1836
1837                // If we failed to generate dependencies...
1838                if(!result)
1839                {
1840                   /* COMMENTED OUT FOR NOW
1841                   f.Printf("$(OBJ)%s.c: %s%s.%s $(Symbols)\n",
1842                      moduleName, modulePath, moduleName, extension);
1843                   */
1844 #endif
1845             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
1846                   f.Printf("$(OBJ)%s.c: %s%s.%s $(OBJ)%s.sym | $(SYMBOLS)\n",
1847                      moduleName, modulePath, moduleName, extension, moduleName);
1848 #if 0
1849                }
1850             }
1851 #endif
1852          /*
1853             f.Printf("\t$(ECC) %s%s.%s $(OBJ)%s.c\n\n",
1854                modulePath, moduleName, extension, moduleName);
1855          */
1856
1857             f.Printf("\t$(ECC)");
1858             // Give priority to file flags
1859             GenFileFlags(f, project, prjConfig);
1860             if(GetECFLAGS(prjConfig))
1861             {
1862                f.Printf("%s $(CECFLAGS)", GetNoLineNumbers(prjConfig) ? " -nolinenumbers" : "");
1863                if(GetMemoryGuard(prjConfig))
1864                   f.Printf(" -memguard");
1865                if(GetStrictNameSpaces(prjConfig))
1866                   f.Printf(" -strictns");
1867                {
1868                   char * s = GetDefaultNameSpace(prjConfig);
1869                   if(s && s[0])
1870                      f.Printf(" -defaultns %s", s);
1871                }
1872             }
1873             else
1874                f.Printf(" $(CECFLAGS) $(ECFLAGS)");
1875             f.Printf(" $(CFLAGS) $(FVISIBILITY)");
1876
1877             f.Printf(" -c %s%s.%s -o $(OBJ)%s.c -symbols $(OBJ)\n\n",
1878                modulePath, moduleName, extension, moduleName);
1879             CloseRulesPlatformExclusionIfs(f, ifCount);
1880          }
1881       }
1882       if(files)
1883       {
1884          bool needed = false;
1885          if(ContainsFilesWithExtension("ec"))
1886          {
1887             for(child : files)
1888             {
1889                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1890                {
1891                   needed = true;
1892                   break;
1893                }
1894             }
1895          }
1896          if(needed)
1897          {
1898             Map<Platform, bool> excludedPlatforms { };
1899             for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
1900             for(platform : platforms)
1901             {
1902                OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
1903                for(child : files)
1904                {
1905                   if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1906                      child.GenMakefilePrintCObjectRules(f, project, compiler, prjConfig, excludedPlatforms);
1907                }
1908             }
1909             CloseRulesPlatformExclusionIfs(f, ifCount);
1910             delete excludedPlatforms;
1911          }
1912       }
1913       delete platforms;
1914    }
1915
1916    void GenMakefilePrintObjectRules(File f, Project project,
1917       Map<String, NameCollisionInfo> namesInfo,
1918       CompilerConfig compiler, ProjectConfig prjConfig,
1919       Map<Platform, bool> parentExcludedPlatforms)
1920    {
1921       int ifCount = 0;
1922       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1923       Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(prjConfig);
1924       //ProjectNode child;
1925       //char objDir[MAX_LOCATION];
1926       //ReplaceSpaces(objDir, config.objDir.dir);
1927       //eSystem_Log("Printing Object Rules\n");
1928       if(type == file)
1929       {
1930          bool collision;
1931          char extension[MAX_EXTENSION];
1932          char modulePath[MAX_LOCATION];
1933          char moduleName[MAX_FILENAME];
1934
1935          GetExtension(name, extension);
1936          /*if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1937                !strcmpi(extension, "ec") || !strcmpi(extension, "cc") ||
1938                !strcmpi(extension, "cxx"))*/
1939          if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1940                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1941                !strcmpi(extension, "m") || !strcmpi(extension, "ec"))
1942          {
1943             DualPipe dep;
1944             char command[2048];
1945             NameCollisionInfo info;
1946
1947             ReplaceSpaces(moduleName, name);
1948             StripExtension(moduleName);
1949
1950             info = namesInfo[moduleName];
1951             collision = info ? info.IsExtensionColliding(extension) : false;
1952             
1953             ReplaceSpaces(modulePath, path);
1954             if(modulePath[0]) strcat(modulePath, SEPS);
1955
1956             // *** Dependency command ***
1957             if(!strcmpi(extension, "ec"))
1958                sprintf(command, "%s -MT $(OBJ)%s.o -MM $(OBJ)%s.c", compiler.ccCommand, moduleName, moduleName);
1959             else
1960                sprintf(command, "%s -MT $(OBJ)%s.o -MM %s%s.%s", compiler.ccCommand, moduleName, modulePath, moduleName, extension);
1961
1962             if(!strcmpi(extension, "ec"))
1963                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1964             else
1965             {
1966 #if 0
1967                // System Includes (from global settings)
1968                for(item : compiler.dirs[includes])
1969                {
1970                   strcat(command, " -isystem ");
1971                   if(strchr(item, ' '))
1972                   {
1973                      strcat(command, "\"");
1974                      strcat(command, item);
1975                      strcat(command, "\"");
1976                   }
1977                   else
1978                      strcat(command, item);
1979                }
1980
1981                for(item : config.includeDirs)
1982                {
1983                   strcat(command, " -I");
1984                   if(strchr(item, ' '))
1985                   {
1986                      strcat(command, "\"");
1987                      strcat(command, item);
1988                      strcat(command, "\"");
1989                   }
1990                   else
1991                      strcat(command, item);
1992                }
1993                for(item : config.preprocessorDefs)
1994                {
1995                   strcat(command, " -D");
1996                   strcat(command, item);
1997                }
1998
1999                // Execute it
2000                if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
2001                {
2002                   char line[1024];
2003                   bool firstLine = true;
2004                   bool result = true;
2005
2006                   // To do some time: auto save external dependencies?
2007
2008                   while(!dep.Eof())
2009                   {
2010                      if(dep.GetLine(line, sizeof(line)-1))
2011                      {
2012                         if(firstLine)
2013                         {
2014                            char * colon = strstr(line, ":");
2015                            if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
2016                            {
2017                               result = false;
2018                               break;
2019                            }
2020                            firstLine = false;
2021                         }
2022                         f.Puts(line);
2023                         f.Puts("\n");
2024                      }
2025                      if(!result) break;
2026                   }
2027                   delete dep;
2028
2029                   // If we failed to generate dependencies...
2030                   if(!result)
2031                   {
2032 #endif
2033             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
2034
2035                      /*if(!strcmpi(extension, "ec"))
2036                         f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
2037                      else*/
2038                         f.Printf("$(OBJ)%s%s%s.o: %s%s.%s\n", moduleName, 
2039                               collision ? "." : "", collision ? extension : "", modulePath, moduleName, extension);
2040 #if 0
2041                   }
2042                }
2043 #endif
2044             }
2045             f.Printf("\t$(CC)");
2046             // Give priority to file flags
2047             GenFileFlags(f, project, prjConfig);
2048
2049             f.Printf(" $(CFLAGS)");
2050
2051             if(!strcmpi(extension, "ec"))
2052                f.Printf(" $(FVISIBILITY) -c $(OBJ)%s.c -o $(OBJ)%s.o\n\n", moduleName, moduleName);
2053             else
2054                f.Printf(" -c %s%s.%s -o $(OBJ)%s%s%s.o\n\n",
2055                      modulePath, moduleName, !strcmpi(extension, "ec") ? "c" : extension, moduleName,
2056                      collision ? "." : "", collision ? extension : "");
2057             CloseRulesPlatformExclusionIfs(f, ifCount);
2058          }
2059       }
2060       if(files)
2061       {
2062          bool needed = false;
2063          for(child : files)
2064          {
2065             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2066             {
2067                needed = true;
2068                break;
2069             }
2070          }
2071          if(needed)
2072          {
2073             Map<Platform, bool> excludedPlatforms { };
2074             for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
2075             for(platform : platforms)
2076             {
2077                OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
2078                for(child : files)
2079                {
2080                   if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2081                      child.GenMakefilePrintObjectRules(f, project, namesInfo, compiler, prjConfig, excludedPlatforms);
2082                }
2083             }
2084             CloseRulesPlatformExclusionIfs(f, ifCount);
2085             delete excludedPlatforms;
2086          }
2087       }
2088       delete platforms;
2089    }
2090
2091    void GenMakefileAddResources(File f, String resourcesPath, ProjectConfig prjConfig)
2092    {
2093       int count = 0;
2094       if(files)
2095       {
2096          int c;
2097          bool prev = false;
2098          //Iterator<ProjectNode> i { files };
2099          //Iterator<ProjectNode> prev { files };
2100          //for(child : files)
2101          //while(i.Next())
2102          for(c = 0; c < files.count; c++)
2103          {
2104             ProjectNode child = files[c];
2105             TwoStrings ts = child.GetPlatformSpecificFu(prjConfig);
2106             if(count > 0 && ts)
2107                prev = true;
2108             if(child.type == file && !child.GetIsExcluded(prjConfig) && !(count > 0 && ts))
2109             {
2110                bool useRes;
2111                char tempPath[MAX_LOCATION];
2112                char resPath[MAX_LOCATION];
2113
2114                char * quotes;
2115
2116                // $(EAR) aw%s --- /*quiet ? "q" : */""
2117                if(count == 0)
2118                   f.Printf("\t%s$(EAR) aw $(TARGET)", ts.a);
2119
2120                tempPath[0] = '\0';
2121                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
2122                {
2123                   useRes = true;
2124                   PathCatSlash(tempPath, child.name);
2125                }
2126                else
2127                {
2128                   useRes = false;
2129                   strcpy(tempPath, child.path);
2130                   PathCatSlash(tempPath, child.name);
2131                }
2132                ReplaceSpaces(resPath, tempPath);
2133                if(strchr(tempPath, ' '))
2134                   quotes = "\"";
2135                else
2136                   quotes = "";
2137                f.Printf(" %s%s%s%s", quotes, useRes ? "$(RES)" : "", tempPath, quotes);
2138                count++;
2139             }
2140             if(count == 10 || (count > 0 && (ts || !child.next)))
2141             {
2142                char path[MAX_LOCATION] = "", temp[MAX_LOCATION];
2143                ProjectNode parent;
2144
2145                for(parent = this; parent.type == folder; parent = parent.parent)
2146                {
2147                   strcpy(temp, path);
2148                   strcpy(path, parent.name);
2149                   if(temp[0])
2150                   {
2151                      strcat(path, "/");
2152                      strcat(path, temp);
2153                   }
2154                }
2155                f.Printf(" \"%s\"%s\n", path, ts.b);
2156                count = 0;
2157                if(prev)
2158                {
2159                   c--;
2160                   prev = false;
2161                }
2162             }
2163             delete ts;
2164          }
2165          for(child : files)
2166          {
2167             if(child.type == folder)
2168                child.GenMakefileAddResources(f, resourcesPath, prjConfig);
2169          }
2170       }
2171    }
2172
2173    Array<Platform> GetPlatformsArrayFromExcluisionInfo(ProjectConfig prjConfig)
2174    {
2175       Array<Platform> platforms { };
2176       Map<Platform, SetBool> exclusionInfo { };
2177       CollectExclusionInfo(exclusionInfo, prjConfig);
2178       if(exclusionInfo[unknown] == true && exclusionInfo.count > 1)
2179          for(mn : exclusionInfo; mn == false)
2180             platforms.Add(&mn);
2181       else
2182          platforms.Add(unknown);
2183       delete exclusionInfo;
2184       return platforms;
2185    }
2186 }
2187
2188 class NameCollisionInfo
2189 {
2190    bool ec;
2191    bool c;
2192    bool cpp;
2193    bool cc;
2194    bool cxx;
2195    bool m;
2196    byte count;
2197
2198    bool IsExtensionColliding(char * extension)
2199    {
2200       bool colliding;
2201       if(count > 1 && ((!strcmpi(extension, "c") && ec) ||
2202             (!strcmpi(extension, "cpp") && (ec || c)) ||
2203             (!strcmpi(extension, "cc") && (ec || c || cpp)) ||
2204             (!strcmpi(extension, "cxx") && (ec || c || cpp || cc)) ||
2205             !strcmpi(extension, "m")))
2206          colliding = true;
2207       else
2208          colliding = false;
2209      return colliding;
2210    }
2211 }
2212
2213 static inline void OpenRulesPlatformExclusionIfs(File f, int * ifCount, Platform platform,
2214       Map<Platform, bool> parentExcludedPlatforms, Map<Platform, bool> excludedPlatforms)
2215 {
2216    if(platform != unknown && !parentExcludedPlatforms[platform])
2217    {
2218       if(*ifCount)                 // we really need a if defined(a) || defined(b) here
2219          f.Printf("else\n");       // instead of repeating the rules for each platform
2220       (*ifCount)++;                  //
2221       f.Printf("ifdef %s\n\n", PlatformToMakefileVariable(platform)); //
2222       if(excludedPlatforms)
2223          excludedPlatforms[platform] = true;
2224    }
2225 }
2226
2227 static inline void CloseRulesPlatformExclusionIfs(File f, int ifCount)
2228 {
2229    if(ifCount)
2230    {
2231       int c;
2232       for(c = 0; c < ifCount; c++)
2233          f.Printf("endif\n");
2234       f.Printf("\n");
2235    }
2236 }