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