ide/Project: Added a distinct C++ compiling/linking command (default to g++) to work...
[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, bool * containsCXX)
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, "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(printType == eCsources)
1431          {
1432             if(!strcmpi(extension, "ec"))
1433             {
1434                char modulePath[MAX_LOCATION];
1435
1436                ReplaceUnwantedMakeChars(modulePath, path);
1437                ReplaceUnwantedMakeChars(moduleName, name);
1438                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
1439                items.Add(CopyString(s));
1440             }
1441          }
1442          else if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1443                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1444                !strcmpi(extension, "m"))
1445          {
1446             if(printType == objects)
1447             {
1448                bool collision;
1449                NameCollisionInfo info;
1450                ReplaceSpaces(moduleName, name);
1451                StripExtension(moduleName);
1452                info = namesInfo[moduleName];
1453                collision = info ? info.IsExtensionColliding(extension) : false;
1454                sprintf(s, "%s$(OBJ)%s%s%s.o%s", ts.a, moduleName, collision ? "." : "", collision ? extension : "", ts.b);
1455                items.Add(CopyString(s));
1456                if(containsCXX && (!strcmpi(extension, "cpp") || !strcmpi(extension, "cc") || !strcmpi(extension, "cxx")))
1457                   *containsCXX = true;
1458             }
1459          }
1460          else if(!strcmpi(extension, "ec"))
1461          {
1462             ReplaceSpaces(moduleName, name);
1463             StripExtension(moduleName);
1464             if(printType == objects)
1465                count++;
1466             s[0] = '\0';
1467             if(printType == objects)
1468                sprintf(s, "%s$(OBJ)%s.o%s", ts.a, moduleName, ts.b);
1469             else if(printType == cObjects)
1470                sprintf(s, "%s$(OBJ)%s.c%s", ts.a, moduleName, ts.b);
1471             else if(printType == symbols)
1472                sprintf(s, "%s$(OBJ)%s.sym%s", ts.a, moduleName, ts.b);
1473             else if(printType == imports)
1474                sprintf(s, "%s$(OBJ)%s.imp%s", ts.a, moduleName, ts.b);
1475             if(s[0])
1476                items.Add(CopyString(s));
1477          }
1478          delete ts;
1479       }
1480       else if(files)
1481       {
1482          for(child : files)
1483          {
1484             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1485                count += child.GenMakefilePrintNode(f, project, printType, namesInfo, items, prjConfig, containsCXX);
1486          }
1487       }
1488       return count;
1489    }
1490
1491    void GenMakefilePrintSymbolRules(File f, Project project, CompilerConfig compiler,
1492          ProjectConfig prjConfig, Map<Platform, bool> parentExcludedPlatforms)
1493    {
1494       int ifCount = 0;
1495       Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(prjConfig);
1496       //ProjectNode child;
1497       //char objDir[MAX_LOCATION];
1498       //ReplaceSpaces(objDir, config.objDir.dir);
1499
1500       //eSystem_Log("Printing Symbol Rules\n");
1501       if(type == file)
1502       {
1503          char extension[MAX_EXTENSION];
1504          char modulePath[MAX_LOCATION];
1505          char moduleName[MAX_FILENAME];
1506
1507          GetExtension(name, extension);
1508          if(!strcmpi(extension, "ec"))
1509          {
1510             DualPipe dep;
1511             char command[2048];
1512
1513             ReplaceSpaces(moduleName, name);
1514             StripExtension(moduleName);
1515
1516             ReplaceSpaces(modulePath, path);
1517             if(modulePath[0]) strcat(modulePath, SEPS);
1518
1519 #if 0
1520             // *** Dependency command ***
1521             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s", moduleName,
1522                modulePath, moduleName, extension);
1523
1524             // System Includes (from global settings)
1525             for(item : compiler.dirs[Includes])
1526             {
1527                strcat(command, " -isystem ");
1528                if(strchr(item, ' '))
1529                {
1530                   strcat(command, "\"");
1531                   strcat(command, item);
1532                   strcat(command, "\"");
1533                }
1534                else
1535                   strcat(command, item);
1536             }
1537
1538             for(item : project.includeDirs)
1539             {
1540                strcat(command, " -I");
1541                if(strchr(item, ' '))
1542                {
1543                   strcat(command, "\"");
1544                   strcat(command, item);
1545                   strcat(command, "\"");
1546                }
1547                else
1548                   strcat(command, item);
1549             }
1550             for(item : project.preprocessorDefs)
1551             {
1552                strcat(command, " -D");
1553                strcat(command, item);
1554             }
1555
1556             // Execute it
1557             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1558             {
1559                char line[1024];
1560                bool firstLine = true;
1561                bool result = true;
1562
1563                // To do some time: auto save external dependencies?
1564                while(!dep.Eof())
1565                {
1566                   if(dep.GetLine(line, sizeof(line)-1))
1567                   {
1568                      if(firstLine)
1569                      {
1570                         char * colon = strstr(line, ":");
1571                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1572                         {
1573                            result = false;
1574                            break;
1575                         }
1576                         firstLine = false;
1577                      }
1578                      f.Puts(line);
1579                      f.Puts("\n");
1580                   }
1581                   if(!result) break;
1582                }
1583                delete dep;
1584
1585                // If we failed to generate dependencies...
1586                if(!result)
1587                {
1588 #endif
1589                OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
1590                   f.Printf("$(OBJ)%s.sym: %s%s.%s\n",
1591                      moduleName, modulePath, moduleName, extension);
1592 #if 0
1593                }
1594             }
1595 #endif
1596          /*
1597             f.Printf("\t$(ECP) %s%s.%s %s.sym\n\n",
1598                modulePath, moduleName, extension, moduleName);
1599             */
1600
1601             f.Printf("\t$(ECP)");
1602             // Give priority to file flags
1603             GenFileFlags(f, project, prjConfig);
1604
1605             f.Printf(" $(CECFLAGS)");
1606             if(GetECFLAGS(prjConfig))
1607             {
1608                if(GetMemoryGuard(prjConfig))
1609                   f.Printf(" -memguard");
1610                if(GetStrictNameSpaces(prjConfig))
1611                   f.Printf(" -strictns");
1612                {
1613                   char * s = GetDefaultNameSpace(prjConfig);
1614                   if(s && s[0])
1615                      f.Printf(" -defaultns %s", s);
1616                }
1617             }
1618             else
1619                f.Printf(" $(ECFLAGS)");
1620             f.Printf(" $(CFLAGS)");
1621
1622             f.Printf(" -c %s%s.%s -o $(OBJ)%s.sym\n\n",
1623                modulePath, moduleName, extension, moduleName);
1624             CloseRulesPlatformExclusionIfs(f, ifCount);
1625          }
1626       }
1627       if(files)
1628       {
1629          bool needed = false;
1630          if(ContainsFilesWithExtension("ec"))
1631          {
1632             for(child : files)
1633             {
1634                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1635                {
1636                   needed = true;
1637                   break;
1638                }
1639             }
1640          }
1641          if(needed)
1642          {
1643             Map<Platform, bool> excludedPlatforms { };
1644             for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
1645             for(platform : platforms)
1646             {
1647                OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
1648                for(child : files)
1649                {
1650                   if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1651                      child.GenMakefilePrintSymbolRules(f, project, compiler, prjConfig, excludedPlatforms);
1652                }
1653             }
1654             CloseRulesPlatformExclusionIfs(f, ifCount);
1655             delete excludedPlatforms;
1656          }
1657       }
1658       delete platforms;
1659    }
1660
1661    void GenMakefilePrintPrepecsRules(File f, Project project, CompilerConfig compiler,
1662          ProjectConfig prjConfig, Map<Platform, bool> parentExcludedPlatforms)
1663    {
1664       int ifCount = 0;
1665       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1666       Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(prjConfig);
1667       //ProjectNode child;
1668       //char objDir[MAX_LOCATION];
1669       //ReplaceSpaces(objDir, config.objDir.dir);
1670
1671       //eSystem_Log("Printing Symbol Rules\n");
1672       if(type == file)
1673       {
1674          char extension[MAX_EXTENSION];
1675          char modulePath[MAX_LOCATION];
1676          char moduleName[MAX_FILENAME];
1677
1678          GetExtension(name, extension);
1679          if(!strcmpi(extension, "ec"))
1680          {
1681             DualPipe dep;
1682             char command[2048];
1683
1684             ReplaceSpaces(moduleName, name);
1685             StripExtension(moduleName);
1686
1687             ReplaceSpaces(modulePath, path);
1688             if(modulePath[0]) strcat(modulePath, SEPS);
1689
1690             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
1691             f.Printf("$(OBJ)%s$(EC): %s%s.%s\n",
1692                moduleName, modulePath, moduleName, extension);
1693             //$(CPP) -x c -E ../extras/gui/controls/DirectoriesBox.ec -o $(OBJ)DirectoriesBox$(EC)
1694             /*f.Printf("\t$(CPP) %s%s.%s %s$(S)\n\n",
1695                modulePath, moduleName, extension, moduleName);*/
1696
1697             f.Printf("\t$(CPP)");
1698             // Give priority to file flags
1699             GenFileFlags(f, project, prjConfig);
1700
1701             /*f.Printf(" $(CECFLAGS)");
1702             if(GetECFLAGS(prjConfig))
1703             {
1704                if(GetMemoryGuard(prjConfig))
1705                   f.Printf(" -memguard");
1706                if(GetStrictNameSpaces(prjConfig))
1707                   f.Printf(" -strictns");
1708                {
1709                   char * s = GetDefaultNameSpace(prjConfig);
1710                   if(s && s[0])
1711                      f.Printf(" -defaultns %s", s);
1712                }
1713             }
1714             else
1715                f.Printf(" $(ECFLAGS)");*/
1716             f.Printf(" $(CFLAGS)");
1717
1718             f.Printf(" -x c -E %s%s.%s -o $(OBJ)%s$(EC)\n\n",
1719                modulePath, moduleName, extension, moduleName);
1720             CloseRulesPlatformExclusionIfs(f, ifCount);
1721          }
1722       }
1723       if(files)
1724       {
1725          bool needed = false;
1726          if(ContainsFilesWithExtension("ec"))
1727          {
1728             for(child : files)
1729             {
1730                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1731                {
1732                   needed = true;
1733                   break;
1734                }
1735             }
1736          }
1737          if(needed)
1738          {
1739             Map<Platform, bool> excludedPlatforms { };
1740             for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
1741             for(platform : platforms)
1742             {
1743                OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
1744                for(child : files)
1745                {
1746                   if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1747                      child.GenMakefilePrintPrepecsRules(f, project, compiler, prjConfig, excludedPlatforms);
1748                }
1749             }
1750             CloseRulesPlatformExclusionIfs(f, ifCount);
1751             delete excludedPlatforms;
1752          }
1753       }
1754       delete platforms;
1755    }
1756
1757    void GenMakefilePrintCObjectRules(File f, Project project, CompilerConfig compiler,
1758       ProjectConfig prjConfig, Map<Platform, bool> parentExcludedPlatforms)
1759    {
1760       int ifCount = 0;
1761       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1762       Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(prjConfig);
1763       //ProjectNode child;
1764       //char objDir[MAX_LOCATION];
1765       //ReplaceSpaces(objDir, config.objDir.dir);
1766       //eSystem_Log("Printing C Object Rules\n");
1767       if(type == file)
1768       {
1769          char extension[MAX_EXTENSION];
1770          char modulePath[MAX_LOCATION];
1771          char moduleName[MAX_FILENAME];
1772
1773          GetExtension(name, extension);
1774          if(!strcmpi(extension, "ec"))
1775          {
1776             DualPipe dep;
1777             char command[2048];
1778
1779             ReplaceSpaces(moduleName, name);
1780             StripExtension(moduleName);
1781
1782             ReplaceSpaces(modulePath, path);
1783             if(modulePath[0]) strcat(modulePath, SEPS);
1784
1785 #if 0
1786             // *** Dependency command ***
1787             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s",
1788                moduleName, modulePath, moduleName, extension);
1789
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 : 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 : 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 result = true;
1827                bool firstLine = true;
1828
1829                // To do some time: auto save external dependencies?
1830                while(!dep.Eof())
1831                {
1832                   if(dep.GetLine(line, sizeof(line)-1))
1833                   {
1834                      if(firstLine)
1835                      {
1836                         char * colon = strstr(line, ":");
1837                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1838                         {
1839                            result = false;
1840                            break;
1841                         }
1842                         firstLine = false;
1843                      }
1844                      f.Puts(line);
1845                      f.Puts("\n");
1846                   }
1847                   if(!result) break;
1848                }
1849                delete dep;
1850
1851                // If we failed to generate dependencies...
1852                if(!result)
1853                {
1854                   /* COMMENTED OUT FOR NOW
1855                   f.Printf("$(OBJ)%s.c: %s%s.%s $(Symbols)\n",
1856                      moduleName, modulePath, moduleName, extension);
1857                   */
1858 #endif
1859             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
1860                   f.Printf("$(OBJ)%s.c: %s%s.%s $(OBJ)%s.sym | $(SYMBOLS)\n",
1861                      moduleName, modulePath, moduleName, extension, moduleName);
1862 #if 0
1863                }
1864             }
1865 #endif
1866          /*
1867             f.Printf("\t$(ECC) %s%s.%s $(OBJ)%s.c\n\n",
1868                modulePath, moduleName, extension, moduleName);
1869          */
1870
1871             f.Printf("\t$(ECC)");
1872             // Give priority to file flags
1873             GenFileFlags(f, project, prjConfig);
1874             if(GetECFLAGS(prjConfig))
1875             {
1876                f.Printf("%s $(CECFLAGS)", GetNoLineNumbers(prjConfig) ? " -nolinenumbers" : "");
1877                if(GetMemoryGuard(prjConfig))
1878                   f.Printf(" -memguard");
1879                if(GetStrictNameSpaces(prjConfig))
1880                   f.Printf(" -strictns");
1881                {
1882                   char * s = GetDefaultNameSpace(prjConfig);
1883                   if(s && s[0])
1884                      f.Printf(" -defaultns %s", s);
1885                }
1886             }
1887             else
1888                f.Printf(" $(CECFLAGS) $(ECFLAGS)");
1889             f.Printf(" $(CFLAGS) $(FVISIBILITY)");
1890
1891             f.Printf(" -c %s%s.%s -o $(OBJ)%s.c -symbols $(OBJ)\n\n",
1892                modulePath, moduleName, extension, moduleName);
1893             CloseRulesPlatformExclusionIfs(f, ifCount);
1894          }
1895       }
1896       if(files)
1897       {
1898          bool needed = false;
1899          if(ContainsFilesWithExtension("ec"))
1900          {
1901             for(child : files)
1902             {
1903                if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1904                {
1905                   needed = true;
1906                   break;
1907                }
1908             }
1909          }
1910          if(needed)
1911          {
1912             Map<Platform, bool> excludedPlatforms { };
1913             for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
1914             for(platform : platforms)
1915             {
1916                OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
1917                for(child : files)
1918                {
1919                   if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
1920                      child.GenMakefilePrintCObjectRules(f, project, compiler, prjConfig, excludedPlatforms);
1921                }
1922             }
1923             CloseRulesPlatformExclusionIfs(f, ifCount);
1924             delete excludedPlatforms;
1925          }
1926       }
1927       delete platforms;
1928    }
1929
1930    void GenMakefilePrintObjectRules(File f, Project project,
1931       Map<String, NameCollisionInfo> namesInfo,
1932       CompilerConfig compiler, ProjectConfig prjConfig,
1933       Map<Platform, bool> parentExcludedPlatforms)
1934    {
1935       int ifCount = 0;
1936       ProjectConfig config = GetMatchingNodeConfig(prjConfig);
1937       Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(prjConfig);
1938       //ProjectNode child;
1939       //char objDir[MAX_LOCATION];
1940       //ReplaceSpaces(objDir, config.objDir.dir);
1941       //eSystem_Log("Printing Object Rules\n");
1942       if(type == file)
1943       {
1944          bool collision;
1945          char extension[MAX_EXTENSION];
1946          char modulePath[MAX_LOCATION];
1947          char moduleName[MAX_FILENAME];
1948
1949          GetExtension(name, extension);
1950          /*if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1951                !strcmpi(extension, "ec") || !strcmpi(extension, "cc") ||
1952                !strcmpi(extension, "cxx"))*/
1953          if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1954                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1955                !strcmpi(extension, "m") || !strcmpi(extension, "ec"))
1956          {
1957             DualPipe dep;
1958             char command[2048];
1959             NameCollisionInfo info;
1960
1961             ReplaceSpaces(moduleName, name);
1962             StripExtension(moduleName);
1963
1964             info = namesInfo[moduleName];
1965             collision = info ? info.IsExtensionColliding(extension) : false;
1966             
1967             ReplaceSpaces(modulePath, path);
1968             if(modulePath[0]) strcat(modulePath, SEPS);
1969
1970             // *** Dependency command ***
1971             if(!strcmpi(extension, "ec"))
1972                sprintf(command, "%s -MT $(OBJ)%s.o -MM $(OBJ)%s.c", compiler.ccCommand, moduleName, moduleName);
1973             else
1974                sprintf(command, "%s -MT $(OBJ)%s.o -MM %s%s.%s", (!strcmpi(extension, "cc") || !strcmpi(extension, "cxx") || !strcmpi(extension, "cpp")) ? compiler.cxxCommand : compiler.ccCommand,
1975                   moduleName, modulePath, moduleName, extension);
1976
1977             if(!strcmpi(extension, "ec"))
1978                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1979             else
1980             {
1981 #if 0
1982                // System Includes (from global settings)
1983                for(item : compiler.dirs[includes])
1984                {
1985                   strcat(command, " -isystem ");
1986                   if(strchr(item, ' '))
1987                   {
1988                      strcat(command, "\"");
1989                      strcat(command, item);
1990                      strcat(command, "\"");
1991                   }
1992                   else
1993                      strcat(command, item);
1994                }
1995
1996                for(item : config.includeDirs)
1997                {
1998                   strcat(command, " -I");
1999                   if(strchr(item, ' '))
2000                   {
2001                      strcat(command, "\"");
2002                      strcat(command, item);
2003                      strcat(command, "\"");
2004                   }
2005                   else
2006                      strcat(command, item);
2007                }
2008                for(item : config.preprocessorDefs)
2009                {
2010                   strcat(command, " -D");
2011                   strcat(command, item);
2012                }
2013
2014                // Execute it
2015                if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
2016                {
2017                   char line[1024];
2018                   bool firstLine = true;
2019                   bool result = true;
2020
2021                   // To do some time: auto save external dependencies?
2022
2023                   while(!dep.Eof())
2024                   {
2025                      if(dep.GetLine(line, sizeof(line)-1))
2026                      {
2027                         if(firstLine)
2028                         {
2029                            char * colon = strstr(line, ":");
2030                            if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
2031                            {
2032                               result = false;
2033                               break;
2034                            }
2035                            firstLine = false;
2036                         }
2037                         f.Puts(line);
2038                         f.Puts("\n");
2039                      }
2040                      if(!result) break;
2041                   }
2042                   delete dep;
2043
2044                   // If we failed to generate dependencies...
2045                   if(!result)
2046                   {
2047 #endif
2048             OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
2049
2050                      /*if(!strcmpi(extension, "ec"))
2051                         f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
2052                      else*/
2053                         f.Printf("$(OBJ)%s%s%s.o: %s%s.%s\n", moduleName, 
2054                               collision ? "." : "", collision ? extension : "", modulePath, moduleName, extension);
2055 #if 0
2056                   }
2057                }
2058 #endif
2059             }
2060             f.Printf("\t$(%s)", (!strcmpi(extension, "cc") || !strcmpi(extension, "cpp") || !strcmpi(extension, "cxx")) ? "CXX" : "CC");
2061             // Give priority to file flags
2062             GenFileFlags(f, project, prjConfig);
2063
2064             f.Printf(" $(CFLAGS)");
2065
2066             if(!strcmpi(extension, "ec"))
2067                f.Printf(" $(FVISIBILITY) -c $(OBJ)%s.c -o $(OBJ)%s.o\n\n", moduleName, moduleName);
2068             else
2069                f.Printf(" -c %s%s.%s -o $(OBJ)%s%s%s.o\n\n",
2070                      modulePath, moduleName, !strcmpi(extension, "ec") ? "c" : extension, moduleName,
2071                      collision ? "." : "", collision ? extension : "");
2072             CloseRulesPlatformExclusionIfs(f, ifCount);
2073          }
2074       }
2075       if(files)
2076       {
2077          bool needed = false;
2078          for(child : files)
2079          {
2080             if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2081             {
2082                needed = true;
2083                break;
2084             }
2085          }
2086          if(needed)
2087          {
2088             Map<Platform, bool> excludedPlatforms { };
2089             for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
2090             for(platform : platforms)
2091             {
2092                OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
2093                for(child : files)
2094                {
2095                   if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
2096                      child.GenMakefilePrintObjectRules(f, project, namesInfo, compiler, prjConfig, excludedPlatforms);
2097                }
2098             }
2099             CloseRulesPlatformExclusionIfs(f, ifCount);
2100             delete excludedPlatforms;
2101          }
2102       }
2103       delete platforms;
2104    }
2105
2106    void GenMakefileAddResources(File f, String resourcesPath, ProjectConfig prjConfig)
2107    {
2108       int count = 0;
2109       if(files)
2110       {
2111          int c;
2112          bool prev = false;
2113          //Iterator<ProjectNode> i { files };
2114          //Iterator<ProjectNode> prev { files };
2115          //for(child : files)
2116          //while(i.Next())
2117          for(c = 0; c < files.count; c++)
2118          {
2119             ProjectNode child = files[c];
2120             TwoStrings ts = child.GetPlatformSpecificFu(prjConfig);
2121             if(count > 0 && ts)
2122                prev = true;
2123             if(child.type == file && !child.GetIsExcluded(prjConfig) && !(count > 0 && ts))
2124             {
2125                bool useRes;
2126                char tempPath[MAX_LOCATION];
2127                char resPath[MAX_LOCATION];
2128
2129                char * quotes;
2130
2131                // $(EAR) aw%s --- /*quiet ? "q" : */""
2132                if(count == 0)
2133                   f.Printf("\t%s$(EAR) aw $(TARGET)", ts.a);
2134
2135                tempPath[0] = '\0';
2136                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
2137                {
2138                   useRes = true;
2139                   PathCatSlash(tempPath, child.name);
2140                }
2141                else
2142                {
2143                   useRes = false;
2144                   strcpy(tempPath, child.path);
2145                   PathCatSlash(tempPath, child.name);
2146                }
2147                ReplaceSpaces(resPath, tempPath);
2148                if(strchr(tempPath, ' '))
2149                   quotes = "\"";
2150                else
2151                   quotes = "";
2152                f.Printf(" %s%s%s%s", quotes, useRes ? "$(RES)" : "", tempPath, quotes);
2153                count++;
2154             }
2155             if(count == 10 || (count > 0 && (ts || !child.next)))
2156             {
2157                char path[MAX_LOCATION] = "", temp[MAX_LOCATION];
2158                ProjectNode parent;
2159
2160                for(parent = this; parent.type == folder; parent = parent.parent)
2161                {
2162                   strcpy(temp, path);
2163                   strcpy(path, parent.name);
2164                   if(temp[0])
2165                   {
2166                      strcat(path, "/");
2167                      strcat(path, temp);
2168                   }
2169                }
2170                f.Printf(" \"%s\"%s\n", path, ts.b);
2171                count = 0;
2172                if(prev)
2173                {
2174                   c--;
2175                   prev = false;
2176                }
2177             }
2178             delete ts;
2179          }
2180          for(child : files)
2181          {
2182             if(child.type == folder)
2183                child.GenMakefileAddResources(f, resourcesPath, prjConfig);
2184          }
2185       }
2186    }
2187
2188    Array<Platform> GetPlatformsArrayFromExcluisionInfo(ProjectConfig prjConfig)
2189    {
2190       Array<Platform> platforms { };
2191       Map<Platform, SetBool> exclusionInfo { };
2192       CollectExclusionInfo(exclusionInfo, prjConfig);
2193       if(exclusionInfo[unknown] == true && exclusionInfo.count > 1)
2194          for(mn : exclusionInfo; mn == false)
2195             platforms.Add(&mn);
2196       else
2197          platforms.Add(unknown);
2198       delete exclusionInfo;
2199       return platforms;
2200    }
2201 }
2202
2203 class NameCollisionInfo
2204 {
2205    bool ec;
2206    bool c;
2207    bool cpp;
2208    bool cc;
2209    bool cxx;
2210    bool m;
2211    byte count;
2212
2213    bool IsExtensionColliding(char * extension)
2214    {
2215       bool colliding;
2216       if(count > 1 && ((!strcmpi(extension, "c") && ec) ||
2217             (!strcmpi(extension, "cpp") && (ec || c)) ||
2218             (!strcmpi(extension, "cc") && (ec || c || cpp)) ||
2219             (!strcmpi(extension, "cxx") && (ec || c || cpp || cc)) ||
2220             !strcmpi(extension, "m")))
2221          colliding = true;
2222       else
2223          colliding = false;
2224      return colliding;
2225    }
2226 }
2227
2228 static inline void OpenRulesPlatformExclusionIfs(File f, int * ifCount, Platform platform,
2229       Map<Platform, bool> parentExcludedPlatforms, Map<Platform, bool> excludedPlatforms)
2230 {
2231    if(platform != unknown && !parentExcludedPlatforms[platform])
2232    {
2233       if(*ifCount)                 // we really need a if defined(a) || defined(b) here
2234          f.Printf("else\n");       // instead of repeating the rules for each platform
2235       (*ifCount)++;                  //
2236       f.Printf("ifdef %s\n\n", PlatformToMakefileVariable(platform)); //
2237       if(excludedPlatforms)
2238          excludedPlatforms[platform] = true;
2239    }
2240 }
2241
2242 static inline void CloseRulesPlatformExclusionIfs(File f, int ifCount)
2243 {
2244    if(ifCount)
2245    {
2246       int c;
2247       for(c = 0; c < ifCount; c++)
2248          f.Printf("endif\n");
2249       f.Printf("\n");
2250    }
2251 }