ide: generate a single makefile, no more compiler specific makefile. added compiler...
[sdk] / ide / src / project / Project.ec
1 #ifdef ECERE_STATIC
2 public import static "ecere"
3 #else
4 public import "ecere"
5 #endif
6
7 #ifndef MAKEFILE_GENERATOR
8 import "ide"
9 // We should have the .sln/.vcproj generation even on other platforms
10 // e.g. detect from an environment variable pointing to a Windows drive
11 #ifdef __WIN32__
12 import "vsSupport"
13 #endif
14 #endif
15
16 import "ProjectConfig"
17 import "ProjectNode"
18 import "IDESettings"
19
20 default:
21
22 static void DummyFunction()
23 {
24 int a;
25 a.OnFree();
26 }
27
28 private:
29
30 extern int __ecereVMethodID_class_OnCompare;
31 extern int __ecereVMethodID_class_OnFree;
32
33 IDESettings ideSettings;
34
35 IDESettingsContainer settingsContainer
36 {
37    driver = "JSON";
38    dataOwner = &ideSettings;
39    dataClass = class(IDESettings);
40
41    void OnLoad(GlobalSettingsData data)
42    {
43       IDESettings settings = (IDESettings)data;
44 #ifndef MAKEFILE_GENERATOR
45       globalSettingsDialog.ideSettings = settings;
46       ide.UpdateRecentMenus();
47       // ide.UpdateMakefiles(); -- can't really regenerate on Load since all recent menus changes will happen
48 #endif
49    }
50 };
51
52 #ifdef MAKEFILE_GENERATOR
53 CompilerConfig defaultCompiler;
54 #endif
55
56 // LEGACY BINARY EPJ LOADING
57
58 SetBool ParseTrueFalseValue(char * string)
59 {
60    if(!strcmpi(string, "True")) return true;
61    return false;
62 }
63
64 void ParseArrayValue(Array<String> array, char * equal)
65 {
66    char * start, * comma;
67    char * string;
68    string = CopyString(equal);
69    start = string;
70    while(start)
71    {
72       comma = strstr(start, ",");
73       if(comma)
74          comma[0] = '\0';
75       array.Add(CopyString(start));
76       if(comma)
77          comma++;
78       if(comma)
79          comma++;
80       start = comma;
81    }
82    delete string;
83 }
84
85 void ProjectNode::LegacyBinaryLoadNode(File f)
86 {
87    int len, count, c;
88    int fileNameLen;
89
90    f.Read(&len, sizeof(len), 1);
91    name = new char[len+1];
92    fileNameLen = len;
93    f.Read(name, sizeof(char), len+1);
94    f.Read(&len, sizeof(len), 1);
95    fileNameLen += len;
96    path = new char[len+1];
97    f.Read(path, sizeof(char), len+1);
98    
99    /*
100    fileName = new char[fileNameLen+2];
101    strcpy(fileName, path);
102    if(fileName[0]) strcat(fileName, "/");
103    strcat(fileName, name);
104    */
105
106    f.Read(&type, sizeof(type), 1);
107    f.Read(&count, sizeof(count), 1);
108    
109    if(type == file)
110    {
111       nodeType = file;
112       icon = NodeIcons::SelectFileIcon(name);
113    }
114    else
115    {
116       nodeType = folder;
117       icon = NodeIcons::SelectNodeIcon(type);
118    }
119
120    if(count && !files) files = { };
121    for(c = 0; c < count; c++)
122    {
123       ProjectNode child { };
124       files.Add(child);
125       child.parent = this;
126       child.indent = indent + 1;
127       LegacyBinaryLoadNode(child, f);
128    }
129 }
130
131 /*void LegacyBinarySaveNode(File f)
132 {
133    int len;
134    ProjectNode child;
135    len = strlen(name);
136    f.Write(&len, sizeof(len), 1);
137    f.Write(name, sizeof(char), len+1);
138
139    if(type == project)
140    {
141       // Projects Absolute Path Are Not Saved
142       len = 0;
143       f.Write(&len, sizeof(len), 1);
144       f.Write(&len, sizeof(char), 1);
145    }
146    else
147    {
148       len = strlen(path);
149       f.Write(&len, sizeof(len), 1);
150       f.Write(path, sizeof(char), len+1);
151    }
152    f.Write(&type, sizeof(type), 1);
153    f.Write(&children.count, sizeof(children.count), 1);
154    for(child = children.first; child; child.next)
155       child.SaveNode(f);
156 }*/
157
158 void ProjectNode::LegacyAsciiSaveNode(File f, char * indentation, char * insidePath)
159 {
160    int len;
161    char printPath[MAX_LOCATION];
162
163    if(type == project && (files.count /*|| preprocessorDefs.first*/))
164    {
165       f.Printf("\n   Files\n");
166    }
167    else if(type == file)
168    {
169       if(!strcmp(path, insidePath))
170          f.Printf("%s - %s\n", indentation, name);
171       else
172       {
173          strcpy(printPath, path);
174          len = strlen(printPath);
175          if(len)
176             if(printPath[len-1] != '/')
177                strcat(printPath, "/");
178          strcat(printPath, name);
179          f.Printf("%s = %s\n", indentation, printPath);
180       }
181       if(files.count)
182          f.Printf("\nError\n\n");
183    }
184    else if(type == folder)
185    {
186       f.Printf("%s + %s\n", indentation, name);
187    }
188    else if(type == resources && files.count)
189    {
190       PathCatSlash(insidePath, path);
191       f.Printf("\n%sResources\n", indentation);
192       if(path && path[0])
193          f.Printf("%s   Path = %s\n", indentation, path);
194    }
195    
196    /*if(buildExclusions.first && type != project)
197    {
198       for(item = buildExclusions.first; item; item = item.next)
199       {
200          if(item == buildExclusions.first)
201             f.Printf("%s      Build Exclusions = %s", indentation, item.name);
202          else
203             f.Printf(", %s", item.name);
204       }
205       f.Printf("\n");
206    }
207
208    if(preprocessorDefs.first && (type == project || ((type == file || type == folder) && !isInResources)))
209    {
210       for(item = preprocessorDefs.first; item; item = item.next)
211       {
212          if(item == preprocessorDefs.first)
213             f.Printf("%s%s   Preprocessor Definitions = %s", indentation, type == project ? "" : "   ", item.name);
214          else
215             f.Printf(", %s", item.name);
216       }
217       f.Printf("\n");
218    }
219    */
220    
221    if(type == project && (files.count /*|| preprocessorDefs.first*/))
222    {
223       f.Printf("\n");
224       for(child : files)
225          LegacyAsciiSaveNode(child, f, indentation, insidePath);
226    }
227    else if(type == folder)
228    {
229       strcat(indentation, "   ");
230       // WHAT WAS THIS: strcpy(printPath, path);
231       // TOCHECK: why is Unix/PathCat not used here
232       len = strlen(insidePath);
233       if(len)
234          if(insidePath[len-1] != '/')
235             strcat(insidePath, "/");
236       strcat(insidePath, name);
237
238       for(child : files)
239          LegacyAsciiSaveNode(child, f, indentation, insidePath);
240       f.Printf("%s -\n", indentation);
241       indentation[strlen(indentation) - 3] = '\0';
242       StripLastDirectory(insidePath, insidePath);
243    }
244    else if(type == resources && files.count)
245    {
246       f.Printf("\n");
247       for(child : files)
248          LegacyAsciiSaveNode(child, f, indentation, insidePath);
249       StripLastDirectory(insidePath, insidePath);
250    }
251 }
252
253 /*
254 void ProjectConfig::LegacyProjectConfigSave(File f)
255 {
256    char * indentation = "      ";
257    //if(isCommonConfig)
258       //f.Printf("\n * Common\n");
259    //else
260       f.Printf("\n + %s\n", name);
261    f.Printf("\n   Compiler\n\n");
262    if(objDir.expression[0])
263       f.Printf("%sIntermediate Directory = %s\n", indentation, objDir.expression);
264    f.Printf("%sDebug = %s\n", indentation, (options.debug == true) ? "True" : "False");
265    switch(options.optimization)
266    {
267       // case none:   f.Printf("%sOptimize = %s\n", indentation, "None"); break;
268       case speed: f.Printf("%sOptimize = %s\n", indentation, "Speed"); break;
269       case size:  f.Printf("%sOptimize = %s\n", indentation, "Size");  break;
270    }
271    f.Printf("%sAllWarnings = %s\n", indentation, (options.warnings == all) ? "True" : "False");
272    if(options.profile)
273       f.Printf("%sProfile = %s\n", indentation, (options.profile == true) ? "True" : "False");
274    if(options.memoryGuard == true)
275       f.Printf("%sMemoryGuard = %s\n", indentation, (options.memoryGuard == true) ? "True" : "False");
276    if(options.strictNameSpaces == true)
277       f.Printf("%sStrict Name Spaces = %s\n", indentation, (options.strictNameSpaces == true) ? "True" : "False");
278    if(options.defaultNameSpace && strlen(options.defaultNameSpace))
279       f.Printf("%sDefault Name Space = %s\n", indentation, options.defaultNameSpace);
280     TOFIX: Compiler Bug {
281       if(options.preprocessorDefinitions.count)
282       {
283          bool isFirst = true;
284          for(item : options.preprocessorDefinitions)
285          {
286             if(isFirst)
287             {
288                f.Printf("\n%sPreprocessor Definitions = %s", indentation, item);
289                isFirst = false;
290             }
291             else
292                f.Printf(", %s", item);
293          }
294          f.Printf("\n");
295       }
296    }
297    if(options.includeDirs.count)
298    {
299       f.Printf("\n%sInclude Directories\n", indentation);
300       for(item : options.includeDirs)
301          f.Printf("%s - %s\n", indentation, name);
302       f.Printf("\n");
303    }
304
305    f.Printf("\n%sLinker\n\n", indentation);
306    f.Printf("%sTarget Name = %s\n", indentation, options.targetFileName);
307    switch(options.targetType)
308    {
309       case executable:     f.Printf("%sTarget Type = %s\n", indentation, "Executable"); break;
310       case sharedLibrary:  f.Printf("%sTarget Type = %s\n", indentation, "Shared");     break;
311       case staticLibrary:  f.Printf("%sTarget Type = %s\n", indentation, "Static");     break;
312    }
313    if(targetDir.expression[0])
314       f.Printf("%sTarget Directory = %s\n", indentation, targetDir.expression);
315    if(options.console)
316       f.Printf("%sConsole = %s\n", indentation, options.console ? "True" : "False");
317    if(options.compress)
318       f.Printf("%sCompress = %s\n", indentation, options.compress ? "True" : "False");
319    if(options.libraries.count)
320    {
321       bool isFirst = true;
322       for(item : options.libraries)
323       {
324          if(isFirst)
325          {
326             f.Printf("\n%sLibraries = %s", indentation, name);
327             isFirst = false;
328          }
329          else
330             f.Printf(", %s", item);
331       }
332       f.Printf("\n");
333    }
334    if(options.libraryDirs.count)
335    {
336       f.Printf("\n%sLibrary Directories\n\n", indentation);
337       for(item : options.libraryDirs)
338          f.Printf("       - %s\n", indentation, item);
339    }
340 }
341
342 void LegacyAsciiSaveProject(File f, Project project)
343 {
344    char indentation[128*3];
345    char path[MAX_LOCATION];
346
347    f.Printf("\nECERE Project File : Format Version 0.1b\n");
348    f.Printf("\nDescription\n%s\n.\n", project.description);
349    f.Printf("\nLicense\n%s\n.\n", project.license);
350    f.Printf("\nModule Name = %s\n", project.moduleName);
351    f.Printf("\n   Configurations\n");
352    //////////////////////////////////////commonConfig.Save(f, true);
353    for(cfg : project.configurations)
354       LegacyProjectConfigSave(cfg, f);
355
356    strcpy(indentation, "   ");
357    path[0] = '\0';
358    LegacyAsciiSaveNode(project.topNode, f, indentation, path);
359    // f.Printf("\n");
360    delete f;
361 }
362 */
363
364 // *** NEW JSON PROJECT FORMAT ***
365 public enum ProjectNodeType { file, folder };
366
367 // *******************************
368
369 define PEEK_RESOLUTION = (18.2 * 10);
370
371 // On Windows & UNIX
372 #define SEPS    "/"
373 #define SEP     '/'
374
375 static byte epjSignature[] = { 'E', 'P', 'J', 0x04, 0x01, 0x12, 0x03, 0x12 };
376
377 enum GenMakefilePrintTypes { objects, cObjects, symbols, imports, sources, resources, eCsources };
378
379 define WorkspaceExtension = "ews";
380 define ProjectExtension = "epj";
381
382 define stringInFileIncludedFrom = "In file included from ";
383
384 void ReplaceSpaces(char * output, char * source)
385 {
386    int c, dc;
387    char ch, pch = 0;
388
389    for(c = 0, dc = 0; (ch = source[c]); c++, dc++)
390    {
391       if(ch == ' ') output[dc++] = '\\';
392       if(pch != '$')
393       {
394          if(ch == '(' || ch == ')') output[dc++] = '\\';
395          pch = ch;
396       }
397       else if(ch == ')')
398          pch = 0;
399       output[dc] = ch;
400    }
401    output[dc] = '\0';
402 }
403
404 void ReplaceUnwantedMakeChars(char * output, char * source)
405 {
406    int c, dc;
407    char ch, pch = 0;
408
409    for(c = 0, dc = 0; (ch = source[c]); c++, dc++)
410    {
411       if(pch != '$')
412       {
413          if(ch == '(' || ch == ')') output[dc++] = '\\';
414          pch = ch;
415       }
416       else if(ch == ')')
417          pch = 0;
418       if(ch == ' ')
419          output[dc] = 127;
420       else
421          output[dc] = ch;
422    }
423    output[dc] = '\0';
424 }
425
426 static void OutputNoSpace(File f, char * source)
427 {
428    char * output = new char[strlen(source)+1024];
429    ReplaceSpaces(output, source);
430    f.Puts(output);
431    delete output;
432 }
433
434 enum ListOutputMethod { inPlace, newLine, lineEach };
435
436 int OutputFileList(File f, char * name, Array<String> list, Map<String, int> varStringLenDiffs, char * prefix)
437 {
438    int numOfBreaks = 0;
439    const int breakListLength = 1536;
440    const int breakLineLength = 78; // TODO: turn this into an option.
441
442    int c, len, itemCount = 0;
443    Array<int> breaks { };
444    if(list.count)
445    {
446       int charCount = 0;
447       MapNode<String, int> mn;
448       for(c=0; c<list.count; c++)
449       {
450          len = strlen(list[c]) + 3;
451          if(strstr(list[c], "$(") && varStringLenDiffs && varStringLenDiffs.count)
452          {
453             for(mn = varStringLenDiffs.root.minimum; mn; mn = mn.next)
454             {
455                if(strstr(list[c], mn.key))
456                   len += mn.value;
457             }
458          }
459          if(charCount + len > breakListLength)
460          {
461             breaks.Add(itemCount);
462             itemCount = 0;
463             charCount = len;
464          }
465          itemCount++;
466          charCount += len;
467       }
468       if(itemCount)
469          breaks.Add(itemCount);
470       numOfBreaks = breaks.count;
471    }
472
473    if(numOfBreaks > 1)
474    {
475       f.Printf("%s =%s%s", name, prefix ? " " : "", prefix ? prefix : "");
476       for(c=0; c<numOfBreaks; c++)
477          f.Printf(" $(%s%d)", name, c+1);
478       f.Printf("\n");
479    }
480    else
481       f.Printf("%s =%s%s", name, prefix ? " " : "", prefix ? prefix : "");
482
483    if(numOfBreaks)
484    {
485       int n, offset = 0;
486
487       for(c=0; c<numOfBreaks; c++)
488       {
489          if(numOfBreaks > 1)
490             f.Printf("%s%d =", name, c+1);
491          
492          len = 3;
493          itemCount = breaks[c];
494          for(n=offset; n<offset+itemCount; n++)
495          {
496             if(false) // TODO: turn this into an option.
497             {
498                int itemLen = strlen(list[n]);
499                if(len > 3 && len + itemLen > breakLineLength)
500                {
501                   f.Printf(" \\\n\t%s", list[n]);
502                   len = 3;
503                }
504                else
505                {
506                   len += itemLen;
507                   f.Printf(" %s", list[n]);
508                }
509             }
510             else
511                f.Printf(" \\\n\t%s", list[n]);
512          }
513          offset += itemCount;
514          f.Printf("\n");
515       }
516       list.Free();
517       list.count = 0;
518    }
519    else
520       f.Printf("\n");
521    f.Printf("\n");
522    delete breaks;
523    return numOfBreaks;
524 }
525
526 void OutputCleanActions(File f, char * name, int parts)
527 {
528    if(parts > 1)
529    {
530       int c;
531       for(c=0; c<parts; c++)
532          f.Printf("\t$(call rmq,$(%s%d))\n", name, c+1);
533    }
534    else
535       f.Printf("\t$(call rmq,$(%s))\n", name);
536 }
537
538 void OutputListOption(File f, char * option, Array<String> list, ListOutputMethod method, bool noSpace)
539 {
540    if(list.count)
541    {
542       if(method == newLine)
543          f.Printf(" \\\n\t");
544       for(item : list)
545       {
546          if(method == lineEach)
547             f.Printf(" \\\n\t");
548          f.Printf(" -%s", option);
549          if(noSpace)
550             OutputNoSpace(f, item);
551          else
552             f.Printf("%s", item);
553       }
554    }
555 }
556
557 static void OutputLibraries(File f, Array<String> libraries)
558 {
559    for(item : libraries)
560    {
561       char ext[MAX_EXTENSION];
562       char temp[MAX_LOCATION];
563       char * s = item;
564       GetExtension(item, ext);
565       if(!strcmp(ext, "o") || !strcmp(ext, "a"))
566          f.Printf(" ");
567       else
568       {
569          if(!strcmp(ext, "so") || !strcmp(ext, "dylib"))
570          {
571             if(!strncmp(item, "lib", 3))
572                strcpy(temp, item + 3);
573             else
574                strcpy(temp, item);
575             StripExtension(temp);
576             s = temp;
577          } 
578          f.Printf(" -l");
579       }
580       OutputNoSpace(f, s);
581    }
582 }
583
584 void CamelCase(char * string)
585 {
586    int c, len = strlen(string);
587    for(c=0; c<len && string[c] >= 'A' && string[c] <= 'Z'; c++)
588       string[c] = (char)tolower(string[c]);
589 }
590
591 CompilerConfig GetCompilerConfig()
592 {
593 #ifndef MAKEFILE_GENERATOR
594    CompilerConfig compiler = null;
595    if(ide && ide.workspace)
596       compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
597    return compiler;
598 #else
599    incref defaultCompiler;
600    return defaultCompiler;
601 #endif
602 }
603
604 define localTargetType = config && config.options && config.options.targetType ?
605             config.options.targetType : options && options.targetType ?
606             options.targetType : TargetTypes::executable;
607 define localWarnings = config && config.options && config.options.warnings ?
608             config.options.warnings : options && options.warnings ?
609             options.warnings : WarningsOption::unset;
610 define localDebug = config && config.options && config.options.debug ?
611             config.options.debug : options && options.debug ?
612             options.debug : SetBool::unset;
613 define localMemoryGuard = config && config.options && config.options.memoryGuard ?
614             config.options.memoryGuard : options && options.memoryGuard ?
615             options.memoryGuard : SetBool::unset;
616 define localNoLineNumbers = config && config.options && config.options.noLineNumbers ?
617             config.options.noLineNumbers : options && options.noLineNumbers ?
618             options.noLineNumbers : SetBool::unset;
619 define localProfile = config && config.options && config.options.profile ?
620             config.options.profile : options && options.profile ?
621             options.profile : SetBool::unset;
622 define localOptimization = config && config.options && config.options.optimization ?
623             config.options.optimization : options && options.optimization ?
624             options.optimization : OptimizationStrategy::none;
625 define localDefaultNameSpace = config && config.options && config.options.defaultNameSpace ?
626             config.options.defaultNameSpace : options && options.defaultNameSpace ?
627             options.defaultNameSpace : null;
628 define localStrictNameSpaces = config && config.options && config.options.strictNameSpaces ?
629             config.options.strictNameSpaces : options && options.strictNameSpaces ?
630             options.strictNameSpaces : SetBool::unset;
631 // TODO: I would rather have null here, check if it'll be ok, have the property return "" if required
632 define localTargetFileName = config && config.options && config.options.targetFileName ?
633             config.options.targetFileName : options && options.targetFileName ?
634             options.targetFileName : "";
635 define localTargetDirectory = config && config.options && config.options.targetDirectory && config.options.targetDirectory[0] ?
636             config.options.targetDirectory : options && options.targetDirectory && options.targetDirectory[0] ?
637             options.targetDirectory : null;
638 define settingsTargetDirectory = ideSettings && ideSettings.projectDefaultIntermediateObjDir &&
639             ideSettings.projectDefaultIntermediateObjDir[0] ?
640             ideSettings.projectDefaultIntermediateObjDir : defaultObjDirExpression;
641 define localObjectsDirectory = config && config.options && config.options.objectsDirectory && config.options.objectsDirectory[0] ?
642             config.options.objectsDirectory : options && options.objectsDirectory && options.objectsDirectory[0] ?
643             options.objectsDirectory : null;
644 define settingsObjectsDirectory = ideSettings && ideSettings.projectDefaultIntermediateObjDir &&
645             ideSettings.projectDefaultIntermediateObjDir[0] ?
646             ideSettings.projectDefaultIntermediateObjDir : defaultObjDirExpression;
647 define localConsole = config && config.options && config.options.console ?
648             config.options.console : options && options.console ?
649             options.console : SetBool::unset;
650 define localCompress = config && config.options && config.options.compress ?
651             config.options.compress : options && options.compress ?
652             options.compress : SetBool::unset;
653
654 define platformTargetType =
655          configPOs && configPOs.options && configPOs.options.targetType && configPOs.options.targetType != localTargetType ?
656                configPOs.options.targetType :
657          projectPOs && projectPOs.options && projectPOs.options.targetType && projectPOs.options.targetType != localTargetType ?
658                projectPOs.options.targetType : TargetTypes::unset;
659
660
661 // we should have some kind of direct mapping between a platform and it's makefile variable
662 char * PlatformToMakefileVariable(Platform platform)
663 {
664    return platform == win32 ? "WINDOWS" :
665           platform == tux   ? "LINUX"   :
666           platform == apple ? "OSX"/*"APPLE"*/ :
667                               platform;
668 }
669
670 char * TargetTypeToMakefileVariable(TargetTypes targetType)
671 {
672    return targetType == executable    ? "executable" :
673           targetType == sharedLibrary ? "sharedlib"  :
674           targetType == staticLibrary ? "staticlib"  :
675                                         "unknown";
676 }
677
678 // Move this to ProjectConfig? null vs Common to consider...
679 char * GetConfigName(ProjectConfig config)
680 {
681    return config ? config.name : "Common";
682 }
683
684 class Project : struct
685 {
686    class_no_expansion;  // To use Find on the Container<Project> in Workspace::projects
687                         // We might want to tweak this default behavior of regular classes ?
688                         // Expansion/the current default kind of Find matching we want for things like BitmapResource, FontResource (Also regular classes)
689 public:
690    float version;
691    String moduleName;
692
693    property ProjectOptions options { get { return options; } set { options = value; } isset { return options && !options.isEmpty; } }
694    property Array<PlatformOptions> platforms
695    {
696       get { return platforms; }
697       set
698       {
699          if(platforms) { platforms.Free(); delete platforms; }
700          if(value)
701          {
702             List<PlatformOptions> empty { };
703             Iterator<PlatformOptions> it { value };
704             platforms = value;
705             for(p : platforms; !p.options || p.options.isEmpty) empty.Add(p);
706             for(p : empty; it.Find(p)) platforms.Delete(it.pointer);
707             delete empty;
708          }
709       }
710       isset
711       {
712          if(platforms)
713          {
714             for(p : platforms)
715             {
716                if(p.options && !p.options.isEmpty)
717                   return true;
718             }
719          }
720          return false;
721       }
722    }
723    List<ProjectConfig> configurations;
724    LinkList<ProjectNode> files;
725    String resourcesPath;
726    LinkList<ProjectNode> resources;
727
728    property char * description
729    {
730       set { delete description; if(value && value[0]) description = CopyString(value); }
731       get { return description ? description : ""; }
732       isset { return description != null && description[0]; }
733    }
734
735    property char * license
736    {
737       set { delete license; if(value && value[0]) license = CopyString(value); }
738       get { return license ? license : ""; }
739       isset { return license != null && license[0]; }
740    }
741
742 private:
743    // topNode.name holds the file name (.epj)
744    ProjectOptions options;
745    Array<PlatformOptions> platforms;
746    ProjectNode topNode { type = project, icon = epjFile, files = LinkList<ProjectNode>{ }, project = this };
747    ProjectNode resNode;
748
749    ProjectConfig config;
750    String filePath;
751    // This is the file name stripped of the epj extension
752    // It should NOT be edited, saved or loaded anywhere
753    String name;
754
755    String description;
756    String license;
757
758    ~Project()
759    {
760       /* // THIS IS NOW AUTOMATED WITH A project CHECK IN ProjectNode
761       topNode.configurations = null;
762       topNode.platforms = null;
763       topNode.options = null;
764       */
765
766       if(platforms) { platforms.Free(); delete platforms; }
767       if(configurations) { configurations.Free(); delete configurations; }
768       if(files) { files.Free(); delete files; }
769       if(resources) { resources.Free(); delete resources; }
770       delete options;
771       delete resourcesPath;
772
773       delete description;
774       delete license;
775       delete moduleName;
776       delete filePath;
777       delete topNode;
778       delete name;
779    }
780
781    property ProjectConfig config
782    {
783       set
784       {
785          config = value;
786          delete topNode.info;
787          topNode.info = CopyString(GetConfigName(config));
788       }
789    }
790    property char * filePath
791    {
792       set
793       {
794          if(value)
795          {
796             char string[MAX_LOCATION];
797             GetLastDirectory(value, string);
798             delete topNode.name;
799             topNode.name = CopyString(string);
800             StripExtension(string);
801             delete name;
802             name = CopyString(string);
803             StripLastDirectory(value, string);
804             delete topNode.path;
805             topNode.path = CopyString(string);
806             delete filePath;
807             filePath = CopyString(value);
808          }
809       }
810    }
811
812    TargetTypes GetTargetType(ProjectConfig config)
813    {
814       TargetTypes targetType = localTargetType;
815       return targetType;
816    }
817
818    bool GetTargetTypeIsSetByPlatform(ProjectConfig config)
819    {
820       Platform platform;
821       for(platform = (Platform)1; platform < Platform::enumSize; platform++)
822       {
823          PlatformOptions projectPOs, configPOs;
824          MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
825          if(platformTargetType)
826             return true;
827       }
828       return false;
829    }
830
831
832    char * GetObjDirExpression(CompilerConfig compiler, ProjectConfig config)
833    {
834       // TODO: Support platform options
835       char * expression = localObjectsDirectory;
836       if(!expression)
837          expression = settingsObjectsDirectory;
838       return expression;
839    }
840
841    DirExpression GetObjDir(CompilerConfig compiler, ProjectConfig config)
842    {
843       char * expression = GetObjDirExpression(compiler, config);
844       DirExpression objDir { type = intermediateObjectsDir };
845       objDir.Evaluate(expression, this, compiler, config);
846       return objDir;
847    }
848
849    char * GetTargetDirExpression(CompilerConfig compiler, ProjectConfig config)
850    {
851       // TODO: Support platform options
852       char * expression = localTargetDirectory;
853       if(!expression)
854          expression = settingsTargetDirectory;
855       return expression;
856    }
857
858    DirExpression GetTargetDir(CompilerConfig compiler, ProjectConfig config)
859    {
860       char * expression = GetTargetDirExpression(compiler, config);
861       DirExpression targetDir { type = DirExpressionType::targetDir /*intermediateObjectsDir*/};
862       targetDir.Evaluate(expression, this, compiler, config);
863       return targetDir;
864    }
865
866    WarningsOption GetWarnings(ProjectConfig config)
867    {
868       WarningsOption warnings = localWarnings;
869       return warnings;
870    }
871
872    bool GetDebug(ProjectConfig config)
873    {
874       SetBool debug = localDebug;
875       return debug == true;
876    }
877
878    bool GetMemoryGuard(ProjectConfig config)
879    {
880       SetBool memoryGuard = localMemoryGuard;
881       return memoryGuard == true;
882    }
883
884    bool GetNoLineNumbers(ProjectConfig config)
885    {
886       SetBool noLineNumbers = localNoLineNumbers;
887       return noLineNumbers == true;
888    }
889
890    bool GetProfile(ProjectConfig config)
891    {
892       SetBool profile = localProfile;
893       return profile == true;
894    }
895
896    OptimizationStrategy GetOptimization(ProjectConfig config)
897    {
898       OptimizationStrategy optimization = localOptimization;
899       return optimization;
900    }
901
902    String GetDefaultNameSpace(ProjectConfig config)
903    {
904       String defaultNameSpace = localDefaultNameSpace;
905       return defaultNameSpace;
906    }
907
908    bool GetStrictNameSpaces(ProjectConfig config)
909    {
910       SetBool strictNameSpaces = localStrictNameSpaces;
911       return strictNameSpaces == true;
912    }
913
914    String GetTargetFileName(ProjectConfig config)
915    {
916       String targetFileName = localTargetFileName;
917       return targetFileName;
918    }
919
920    //String targetDirectory;
921    //String objectsDirectory;
922    bool GetConsole(ProjectConfig config)
923    {
924       SetBool console = localConsole;
925       return console == true;
926    }
927
928    bool GetCompress(ProjectConfig config)
929    {
930       SetBool compress = localCompress;
931       return compress == true;
932    }
933    //SetBool excludeFromBuild;
934
935    bool GetConfigIsInActiveDebugSession(ProjectConfig config)
936    {
937 #ifndef MAKEFILE_GENERATOR
938       return ide.project == this && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isActive;
939 #endif
940    }
941
942    bool GetConfigIsInDebugSession(ProjectConfig config)
943    {
944 #ifndef MAKEFILE_GENERATOR
945       return ide.project == this && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isPrepared;
946 #endif
947    }
948
949    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config)
950    {
951 #ifndef MAKEFILE_GENERATOR
952       ide.SetPath(projectsDirs, compiler, config);
953 #endif
954    }
955
956 #ifndef MAKEFILE_GENERATOR
957    bool Save(char * fileName)
958    {
959       File f;
960       /*char output[MAX_LOCATION];
961        ChangeExtension(fileName, "json", output);
962       f = FileOpen(output, write);*/
963       f = FileOpen(fileName, write);
964       if(f)
965       {
966          files = topNode.files;
967          resources = resNode.files;
968          resourcesPath = resNode.path;
969
970          files.Remove(resNode);
971          version = 0.2f;
972
973          WriteJSONObject(f, class(Project), this, 0);
974
975          files.Add(resNode);
976
977          files = null;
978          resources = null;
979          resourcesPath = null;
980          delete f;
981       }
982       return true;
983    }
984 #endif
985
986 #ifndef MAKEFILE_GENERATOR
987    bool GetRelativePath(char * filePath, char * relativePath)
988    {
989       ProjectNode node;
990       char moduleName[MAX_FILENAME];
991       GetLastDirectory(filePath, moduleName);
992       // try with workspace dir first?
993       if((node = topNode.Find(moduleName, false)))
994       {
995          strcpy(relativePath, strcmp(node.path, ".") ? node.path : "");
996          PathCatSlash(relativePath, node.name);
997          return true;
998       }
999       // WARNING: On failure, relative path is uninitialized
1000       return false;   
1001    }
1002 #endif
1003
1004    void CatTargetFileName(char * string, CompilerConfig compiler, ProjectConfig config)
1005    {
1006       TargetTypes targetType = GetTargetType(config);
1007       String targetFileName = GetTargetFileName(config);
1008       if(targetType == staticLibrary)
1009       {
1010          PathCatSlash(string, "lib");
1011          strcat(string, targetFileName);
1012       }
1013       else if(compiler.targetPlatform != win32 && targetType == sharedLibrary)
1014       {
1015          PathCatSlash(string, "lib");
1016          strcat(string, targetFileName);
1017       }
1018       else
1019          PathCatSlash(string, targetFileName);
1020       
1021       switch(targetType)
1022       {
1023          case executable:
1024             if(compiler.targetPlatform == win32)
1025                strcat(string, ".exe");
1026             break;
1027          case sharedLibrary:
1028             if(compiler.targetPlatform == win32)
1029                strcat(string, ".dll");
1030             else if(compiler.targetPlatform == apple)
1031                strcat(string, ".dylib");
1032             else
1033                strcat(string, ".so");
1034             break;
1035          case staticLibrary:
1036             strcat(string, ".a");
1037             break;
1038       }
1039    }
1040
1041    void GetCompilerConfigsDir(char * cfDir)
1042    {
1043       strcpy(cfDir, topNode.path);
1044       PathCatSlash(cfDir, ideSettings.compilerConfigsDir);
1045       if(cfDir && cfDir[0] && cfDir[strlen(cfDir)-1] != '/')
1046          strcat(cfDir, "/");
1047    }
1048
1049    void CatMakeFileName(char * string, ProjectConfig config)
1050    {
1051       char projectName[MAX_LOCATION];
1052       strcpy(projectName, name);
1053       sprintf(string, "%s%s%s.Makefile", projectName, config ? "-" : "", config ? config.name : "");
1054    }
1055
1056 #ifndef MAKEFILE_GENERATOR
1057    void MarkChanges(ProjectNode node)
1058    {
1059       for(cfg : topNode.configurations)
1060       {
1061          ProjectConfig c = null;
1062          for(i : node.configurations; !strcmpi(i.name, cfg.name)) { c = i; break; }
1063
1064          if(c && ((c.options && cfg.options && cfg.options.console != c.options.console) ||
1065                (!c.options || !cfg.options)))
1066             cfg.symbolGenModified = true;
1067
1068          cfg.makingModified = true;
1069       }
1070    }
1071
1072    void ModifiedAllConfigs(bool making, bool compiling, bool linking, bool symbolGen)
1073    {
1074       for(cfg : configurations)
1075       {
1076          if(making)
1077             cfg.makingModified = true;
1078          if(compiling)
1079             cfg.compilingModified = true;
1080          if(linking)
1081             cfg.linkingModified = true;
1082          if(symbolGen)
1083             cfg.symbolGenModified = true;
1084       }
1085       if(compiling || linking)
1086       {
1087          ide.projectView.modifiedDocument = true;
1088          ide.workspace.modified = true;
1089       }
1090    }
1091    
1092    void RotateActiveConfig(bool forward)
1093    {
1094       if(configurations.first && configurations.last != configurations.first)
1095       {
1096          Iterator<ProjectConfig> cfg { configurations };
1097          while(forward ? cfg.Next() : cfg.Prev())
1098             if(cfg.data == config)
1099                break;
1100
1101          if(forward)
1102          {
1103             if(!cfg.Next())
1104                cfg.Next();
1105          }
1106          else
1107          {
1108             if(!cfg.Prev())
1109                cfg.Prev();
1110          }
1111
1112          property::config = cfg.data;
1113          ide.workspace.modified = true;
1114          ide.projectView.Update(null);
1115       }
1116    }
1117
1118    void ProcessPipeOutputRaw(DualPipe f)
1119    {
1120       char line[65536];
1121       while(!f.Eof() && !ide.ShouldStopBuild())
1122       {
1123          bool result = true;
1124          double lastTime = GetTime();
1125          bool wait = true;
1126          while(result)
1127          {
1128             if((result = f.Peek()) && (result = f.GetLine(line, sizeof(line)-1)))
1129             {
1130                ide.outputView.buildBox.Logf("%s\n", line);
1131             }
1132             if(GetTime() - lastTime > 1.0 / PEEK_RESOLUTION) break;
1133          }
1134          //printf("Processing Input...\n");
1135          if(app.ProcessInput(true))
1136             wait = false;
1137          app.UpdateDisplay();
1138          if(wait)
1139          {
1140             //printf("Waiting...\n");
1141             app.Wait();
1142          }
1143          //if(!result) Sleep(1.0 / PEEK_RESOLUTION);
1144       }
1145       if(ide.ShouldStopBuild())
1146       {
1147          ide.outputView.buildBox.Logf($"\nBuild cancelled by user.\n", line);
1148          f.Terminate();
1149       }
1150    }
1151
1152    bool ProcessBuildPipeOutput(DualPipe f, DirExpression objDirExp, bool isARun, ProjectNode onlyNode,
1153       CompilerConfig compiler, ProjectConfig config)
1154    {
1155       char line[65536];
1156       bool compiling = false, linking = false, precompiling = false;
1157       int compilingEC = 0;
1158       int numErrors = 0, numWarnings = 0;
1159       bool loggedALine = false;
1160       char * configName = config.name;
1161       int lenMakeCommand = strlen(compiler.makeCommand);
1162
1163       char cppCommand[MAX_LOCATION];
1164       char ccCommand[MAX_LOCATION];
1165       char ecpCommand[MAX_LOCATION];
1166       char eccCommand[MAX_LOCATION];
1167       char ecsCommand[MAX_LOCATION];
1168       char earCommand[MAX_LOCATION];
1169
1170       char * cc = compiler.ccCommand;
1171       char * cpp = compiler.cppCommand;
1172       sprintf(cppCommand, "%s%s%s%s ",
1173             compiler.ccacheEnabled ? "ccache " : "",
1174             compiler.ccacheEnabled && !compiler.distccEnabled ? " " : "",
1175             compiler.distccEnabled ? "distcc " : "",
1176             compiler.cppCommand);
1177       sprintf(ccCommand, "%s%s%s%s ",
1178             compiler.ccacheEnabled ? "ccache " : "",
1179             compiler.ccacheEnabled && !compiler.distccEnabled ? " " : "",
1180             compiler.distccEnabled ? "distcc " : "",
1181             compiler.ccCommand);
1182       sprintf(ecpCommand, "%s ", compiler.ecpCommand);
1183       sprintf(eccCommand, "%s ", compiler.eccCommand);
1184       sprintf(ecsCommand, "%s ", compiler.ecsCommand);
1185       sprintf(earCommand, "%s ", compiler.earCommand);
1186
1187       while(!f.Eof() && !ide.ShouldStopBuild())
1188       {
1189          bool result = true;
1190          double lastTime = GetTime();
1191          bool wait = true;
1192          while(result)
1193          {
1194             //printf("Peeking and GetLine...\n");
1195             if((result = f.Peek()) && (result = f.GetLine(line, sizeof(line)-1)))
1196             {
1197                char * inFileIncludedFrom = strstr(line, stringInFileIncludedFrom);
1198                if(strstr(line, compiler.makeCommand) == line && line[lenMakeCommand] == ':')
1199                {
1200                   char * module = strstr(line, "No rule to make target `");
1201                   if(module)
1202                   {
1203                      char * end;
1204                      module = strchr(module, '`') + 1;
1205                      end = strchr(module, '\'');
1206                      if(end)
1207                      {
1208                         *end = '\0';
1209                         ide.outputView.buildBox.Logf($"   %s: No such file or directory\n", module);
1210                         // ide.outputView.buildBox.Logf("error: %s\n   No such file or directory\n", module);
1211                         numErrors++;
1212                      }
1213                   }
1214                   //else
1215                   //{
1216                      //ide.outputView.buildBox.Logf("error: %s\n", line);
1217                      //numErrors++;
1218                   //}
1219                }
1220                else if(strstr(line, "ear ") == line);
1221                else if(strstr(line, "strip ") == line);
1222                else if(strstr(line, ccCommand) == line || strstr(line, ecpCommand) == line || strstr(line, eccCommand) == line)
1223                {
1224                   char moduleName[MAX_FILENAME];
1225                   byte * tokens[1];
1226                   char * module;
1227                   bool isPrecomp = false;
1228
1229                   if(strstr(line, ccCommand) == line)
1230                   {
1231                      module = strstr(line, " -c ");
1232                      if(module) module += 4;
1233                   }
1234                   else if(strstr(line, eccCommand) == line)
1235                   {
1236                      module = strstr(line, " -c ");
1237                      if(module) module += 4;
1238                      //module = line + 3;
1239                      // Don't show GCC warnings about generated C code because it does not compile clean yet...
1240                      compilingEC = 3;//2;
1241                   }
1242                   else if(strstr(line, ecpCommand) == line)
1243                   {
1244                      // module = line + 8;
1245                      module = strstr(line, " -c ");
1246                      if(module) module += 4;
1247                      isPrecomp = true;
1248                      compilingEC = 0;
1249                   }
1250
1251                   loggedALine = true;
1252
1253                   if(module)
1254                   {
1255                      if(!compiling && !isPrecomp)
1256                      {
1257                         ide.outputView.buildBox.Logf($"Compiling...\n");
1258                         compiling = true;
1259                      }
1260                      else if(!precompiling && isPrecomp)
1261                      {
1262                         ide.outputView.buildBox.Logf($"Generating symbols...\n");
1263                         precompiling = true;
1264                      }
1265                      // Changed escapeBackSlashes here to handle paths with spaces
1266                      Tokenize(module, 1, tokens, true); // false);
1267                      GetLastDirectory(module, moduleName);
1268                      ide.outputView.buildBox.Logf("%s\n", moduleName);
1269                   }
1270                   else if((module = strstr(line, " -o ")))
1271                   {
1272                      compiling = false;
1273                      precompiling = false;
1274                      linking = true;
1275                      ide.outputView.buildBox.Logf($"Linking...\n");
1276                   }
1277                   else
1278                   {
1279                      ide.outputView.buildBox.Logf("%s\n", line);
1280                      numErrors++;
1281                   }
1282
1283                   if(compilingEC) compilingEC--;
1284                }
1285                else if(strstr(line, "ar rcs") == line)
1286                   ide.outputView.buildBox.Logf($"Building library...\n");
1287                else if(strstr(line, ecsCommand) == line)
1288                   ide.outputView.buildBox.Logf($"Writing symbol loader...\n");
1289                else
1290                {
1291                   if(linking || compiling || precompiling)
1292                   {
1293                      char * colon = strstr(line, ":"); //, * bracket;
1294                      if(colon && (colon[1] == '/' || colon[1] == '\\'))
1295                         colon = strstr(colon + 1, ":");
1296                      if(colon)
1297                      {
1298                         char moduleName[MAX_LOCATION], temp[MAX_LOCATION];
1299                         char * pointer;
1300                         char * error;
1301                         char * start = inFileIncludedFrom ? line + strlen(stringInFileIncludedFrom) : line;
1302                         int len = (int)(colon - start);
1303                         len = Min(len, MAX_LOCATION-1);
1304                         // Don't be mistaken by the drive letter colon
1305                         // Cut module name
1306                         // TODO: need to fix colon - line gives char *
1307                         // warning: incompatible expression colon - line (char *); expected int
1308                         /*
1309                         strncpy(moduleName, line, (int)(colon - line));
1310                         moduleName[colon - line] = '\0';
1311                         */
1312                         strncpy(moduleName, start, len);
1313                         moduleName[len] = '\0';
1314                         // Remove stuff in brackets
1315                         //bracket = strstr(moduleName, "(");
1316                         //if(bracket) *bracket = '\0';
1317
1318                         GetLastDirectory(moduleName, temp);
1319                         if(linking && (!strcmp(temp, "ld") || !strcmp(temp, "ld.exe")))
1320                         {
1321                            numErrors++;
1322                            strcpy(moduleName, $"Linker Error");
1323                         }
1324                         else
1325                         {
1326                            strcpy(temp, topNode.path);
1327                            PathCatSlash(temp, moduleName);
1328                            MakePathRelative(temp, topNode.path, moduleName);
1329                         }
1330                         if(strstr(line, "error:"))
1331                            numErrors ++;
1332                         else
1333                         {
1334                            // Silence warnings for compiled EC
1335                            char * objDir = strstr(moduleName, objDirExp.dir);
1336                         
1337                            if(linking)
1338                            {
1339                               if((pointer = strstr(line, "undefined"))  ||
1340                                    (pointer = strstr(line, "No such file")) ||
1341                                    (pointer = strstr(line, "token")))
1342                               {
1343                                  strncat(moduleName, colon, pointer - colon);
1344                                  strcat(moduleName, "error: ");
1345                                  colon = pointer;
1346                                  numErrors ++;
1347                               }
1348                            }
1349                            else if((pointer = strstr(line, "No such file")))
1350                            {
1351                               strncat(moduleName, colon, pointer - colon);
1352                               strcat(moduleName, "error: ");
1353                               colon = pointer;
1354                               numErrors ++;
1355                            }
1356                            else if(compilingEC == 1 || (objDir && objDir == moduleName))
1357                               continue;
1358                            else if(strstr(line, "warning:"))
1359                            {
1360                               numWarnings++;
1361                            }
1362                         }
1363                         if(this == ide.workspace.projects.firstIterator.data)
1364                            ide.outputView.buildBox.Logf("   %s%s\n", moduleName, colon);
1365                         else
1366                         {
1367                            char fullModuleName[MAX_LOCATION];
1368                            strcpy(fullModuleName, topNode.path);
1369                            PathCat(fullModuleName, moduleName);
1370                            MakePathRelative(fullModuleName, ide.workspace.projects.firstIterator.data.topNode.path, fullModuleName);
1371                            MakeSystemPath(fullModuleName);
1372                            ide.outputView.buildBox.Logf("   %s%s%s\n", inFileIncludedFrom ? stringInFileIncludedFrom : "", fullModuleName, colon);
1373                         }
1374                      }
1375                      else
1376                      {
1377                         ide.outputView.buildBox.Logf("%s\n", line);
1378                         linking = compiling = precompiling = false;
1379                      }
1380                   }
1381                   else
1382                      ide.outputView.buildBox.Logf("%s\n", line);
1383                }
1384                wait = false;
1385             }
1386             //printf("Done getting line\n");
1387             if(GetTime() - lastTime > 1.0 / PEEK_RESOLUTION) break;
1388          }
1389          //printf("Processing Input...\n");
1390          if(app.ProcessInput(true))
1391             wait = false;
1392          app.UpdateDisplay();
1393          if(wait)
1394          {
1395             //printf("Waiting...\n");
1396             app.Wait();
1397          }
1398          //if(!result) Sleep(1.0 / PEEK_RESOLUTION);
1399       }
1400       if(ide.ShouldStopBuild())
1401       {
1402          ide.outputView.buildBox.Logf($"\nBuild cancelled by user.\n", line);
1403          f.Terminate();
1404       }
1405       else if(loggedALine || !isARun)
1406       {
1407          if(f.GetExitCode() && !numErrors)
1408          {
1409             bool result = f.GetLine(line, sizeof(line)-1);
1410             ide.outputView.buildBox.Logf($"Fatal Error: child process terminated unexpectedly\n");
1411          }
1412          else
1413          {
1414             if(!onlyNode)
1415                ide.outputView.buildBox.Logf("\n%s (%s) - ", GetTargetFileName(config), configName);
1416             if(numErrors)
1417                ide.outputView.buildBox.Logf("%d %s, ", numErrors, (numErrors > 1) ? $"errors" : $"error");
1418             else
1419                ide.outputView.buildBox.Logf($"no error, ");
1420    
1421             if(numWarnings)
1422                ide.outputView.buildBox.Logf("%d %s\n", numWarnings, (numWarnings > 1) ? $"warnings" : $"warning");
1423             else
1424                ide.outputView.buildBox.Logf($"no warning\n");
1425          }
1426       }
1427       return numErrors == 0;
1428    }
1429
1430    void ProcessCleanPipeOutput(DualPipe f, CompilerConfig compiler, ProjectConfig config)
1431    {
1432       char line[65536];
1433       int lenMakeCommand = strlen(compiler.makeCommand);
1434       while(!f.Eof())
1435       {
1436          bool result = true;
1437          bool wait = true;
1438          double lastTime = GetTime();
1439          while(result)
1440          {
1441             if((result = f.Peek()) && (result = f.GetLine(line, sizeof(line)-1)))
1442             {
1443                if(strstr(line, compiler.makeCommand) == line && line[lenMakeCommand] == ':');
1444                else if(strstr(line, "del") == line);
1445                else if(strstr(line, "rm") == line);
1446                else if(strstr(line, "Could Not Find") == line);
1447                else
1448                {
1449                   ide.outputView.buildBox.Logf(line);
1450                   ide.outputView.buildBox.Logf("\n");
1451                }
1452                wait = false;
1453             }
1454             if(GetTime() - lastTime > 1.0 / PEEK_RESOLUTION) break;
1455          }
1456          if(app.ProcessInput(true))
1457             wait = false;
1458          app.UpdateDisplay();
1459          if(wait)
1460             app.Wait();
1461          //Sleep(1.0 / PEEK_RESOLUTION);
1462       }
1463    }
1464
1465    bool Build(bool isARun, ProjectNode onlyNode, CompilerConfig compiler, ProjectConfig config)
1466    {
1467       bool result = false;
1468       DualPipe f;
1469       char targetFileName[MAX_LOCATION] = "";
1470       char makeTarget[MAX_LOCATION] = "";
1471       char makeFile[MAX_LOCATION];
1472       char makeFilePath[MAX_LOCATION];
1473       char configName[MAX_LOCATION];
1474       DirExpression objDirExp = GetObjDir(compiler, config);
1475       PathBackup pathBackup { };
1476
1477       int numJobs = compiler.numJobs;
1478       char command[MAX_LOCATION];
1479       char * compilerName;
1480
1481       compilerName = CopyString(compiler.name);
1482       CamelCase(compilerName);
1483
1484       strcpy(configName, config ? config.name : "Common");
1485
1486       SetPath(false, compiler, config); //true
1487       CatTargetFileName(targetFileName, compiler, config);
1488
1489       strcpy(makeFilePath, topNode.path);
1490       CatMakeFileName(makeFile, config);
1491       PathCatSlash(makeFilePath, makeFile);
1492       
1493       // TODO: TEST ON UNIX IF \" around makeTarget is ok
1494       if(onlyNode)
1495       {
1496          if(compiler.type.isVC)
1497          {
1498             PrintLn("compiling a single file is not yet supported");
1499          }
1500          else
1501          {
1502             int len;
1503             char pushD[MAX_LOCATION];
1504             char cfDir[MAX_LOCATION];
1505             GetCompilerConfigsDir(cfDir);
1506             GetWorkingDir(pushD, sizeof(pushD));
1507             ChangeWorkingDir(topNode.path);
1508             // Create object dir if it does not exist already
1509             if(!FileExists(objDirExp.dir).isDirectory)
1510                Execute("%s E_IDE_CF_DIR=%s COMPILER=%s objdir -C \"%s\" -f \"%s\"", compiler.makeCommand, cfDir, compilerName, topNode.path, makeFilePath);
1511             ChangeWorkingDir(pushD);
1512
1513             PathCatSlash(makeTarget+1, objDirExp.dir);
1514             PathCatSlash(makeTarget+1, onlyNode.name);
1515             StripExtension(makeTarget+1);
1516             strcat(makeTarget+1, ".o");
1517             makeTarget[0] = '\"';
1518             len = strlen(makeTarget);
1519             makeTarget[len++] = '\"';
1520             makeTarget[len++] = '\0';
1521          }
1522       }
1523
1524       if(compiler.type.isVC)
1525       {
1526          bool result = false;
1527          char oldwd[MAX_LOCATION];
1528          GetWorkingDir(oldwd, sizeof(oldwd));
1529          ChangeWorkingDir(topNode.path);
1530
1531          sprintf(command, "%s /useenv /nologo /logcommands %s.sln %s|Win32", compiler.makeCommand, name, config.name);
1532          ide.outputView.buildBox.Logf("command: %s\n", command);
1533          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
1534          {
1535             ProcessPipeOutputRaw(f);
1536             delete f;
1537             result = true;
1538          }
1539          ChangeWorkingDir(oldwd);
1540       }
1541       else
1542       {
1543          char cfDir[MAX_LOCATION];
1544          GetCompilerConfigsDir(cfDir);
1545          sprintf(command, "%s E_IDE_CF_DIR=%s COMPILER=%s -j%d %s%s%s -C \"%s\" -f \"%s\"", compiler.makeCommand, cfDir, compilerName, numJobs,
1546                compiler.ccacheEnabled ? "CCACHE=y " : "",
1547                compiler.distccEnabled ? "DISTCC=y " : "",
1548                makeTarget, topNode.path, makeFilePath);
1549          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
1550          {
1551             result = ProcessBuildPipeOutput(f, objDirExp, isARun, onlyNode, compiler, config);
1552             delete f;
1553          }
1554          else
1555             ide.outputView.buildBox.Logf($"Error executing make (%s) command\n", compiler.makeCommand);
1556       }
1557
1558       delete pathBackup;
1559       delete objDirExp;
1560       delete compilerName;
1561       return result;
1562    }
1563
1564    void Clean(CompilerConfig compiler, ProjectConfig config, bool realclean)
1565    {
1566       char makeFile[MAX_LOCATION];
1567       char makeFilePath[MAX_LOCATION];
1568       char command[MAX_LOCATION];
1569       char * compilerName;
1570       DualPipe f;
1571       PathBackup pathBackup { };
1572
1573       compilerName = CopyString(compiler.name);
1574       CamelCase(compilerName);
1575
1576       SetPath(false, compiler, config);
1577
1578       strcpy(makeFilePath, topNode.path);
1579       CatMakeFileName(makeFile, config);
1580       PathCatSlash(makeFilePath, makeFile);
1581       
1582       if(compiler.type.isVC)
1583       {
1584          bool result = false;
1585          char oldwd[MAX_LOCATION];
1586          GetWorkingDir(oldwd, sizeof(oldwd));
1587          ChangeWorkingDir(topNode.path);
1588          
1589          sprintf(command, "%s /useenv /clean /nologo /logcommands %s.sln %s|Win32", compiler.makeCommand, name, config.name);
1590          ide.outputView.buildBox.Logf("command: %s\n", command);
1591          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
1592          {
1593             ProcessPipeOutputRaw(f);
1594             delete f;
1595             result = true;
1596          }
1597          ChangeWorkingDir(oldwd);
1598          return result;
1599       }
1600       else
1601       {
1602          char cfDir[MAX_LOCATION];
1603          GetCompilerConfigsDir(cfDir);
1604          sprintf(command, "%s E_IDE_CF_DIR=%s COMPILER=%s %sclean -C \"%s\" -f \"%s\"", compiler.makeCommand, cfDir, compilerName, realclean ? "real" : "", topNode.path, makeFilePath);
1605          if((f = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1606          {
1607             ide.outputView.buildBox.Tell($"Deleting target and object files...");
1608             ProcessCleanPipeOutput(f, compiler, config);
1609             delete f;
1610
1611             ide.outputView.buildBox.Logf($"Target and object files deleted\n");
1612          }
1613       }
1614
1615       delete pathBackup;
1616       delete compilerName;
1617    }
1618
1619    void Run(char * args, CompilerConfig compiler, ProjectConfig config)
1620    {   
1621       String target = new char[maxPathLen];
1622       char oldDirectory[MAX_LOCATION];
1623       DirExpression targetDirExp = GetTargetDir(compiler, config);
1624       PathBackup pathBackup { };
1625
1626       // Build(project, ideMain, true, null);
1627
1628    #if defined(__WIN32__)
1629       strcpy(target, topNode.path);
1630    #else
1631       strcpy(target, "");
1632    #endif
1633       PathCatSlash(target, targetDirExp.dir);
1634       CatTargetFileName(target, compiler, config);
1635       sprintf(target, "%s %s", target, args);
1636       GetWorkingDir(oldDirectory, MAX_LOCATION);
1637
1638       if(strlen(ide.workspace.debugDir))
1639       {
1640          char temp[MAX_LOCATION];
1641          strcpy(temp, topNode.path);
1642          PathCatSlash(temp, ide.workspace.debugDir);
1643          ChangeWorkingDir(temp);
1644       }
1645       else
1646          ChangeWorkingDir(topNode.path);
1647       // ChangeWorkingDir(topNode.path);
1648       SetPath(true, compiler, config);
1649       if(compiler.execPrefixCommand)
1650       {
1651          char * prefixedTarget = new char[strlen(compiler.execPrefixCommand) + strlen(target) + 2];
1652          prefixedTarget[0] = '\0';
1653          strcat(prefixedTarget, compiler.execPrefixCommand);
1654          strcat(prefixedTarget, " ");
1655          strcat(prefixedTarget, target);
1656          Execute(prefixedTarget);
1657          delete prefixedTarget;
1658       }
1659       else
1660          Execute(target);
1661
1662       ChangeWorkingDir(oldDirectory);
1663       delete pathBackup;
1664
1665       delete targetDirExp;
1666       delete target;
1667    }
1668
1669    void Compile(ProjectNode node, CompilerConfig compiler, ProjectConfig config)
1670    {
1671       Build(false, node, compiler, config);
1672    }
1673 #endif
1674
1675    void GetMakefileTargetFileName(TargetTypes targetType, char * fileName, ProjectConfig config)
1676    {
1677       fileName[0] = '\0';
1678       if(targetType == staticLibrary || targetType == sharedLibrary)
1679          strcat(fileName, "$(LP)");
1680       // !!! ReplaceSpaces must be done after all PathCat calls !!!
1681       // ReplaceSpaces(s, GetTargetFileName(config));
1682       strcat(fileName, GetTargetFileName(config));
1683       switch(targetType)
1684       {
1685          case executable:
1686             strcat(fileName, "$(E)");
1687             break;
1688          case sharedLibrary:
1689             strcat(fileName, "$(SO)");
1690             break;
1691          case staticLibrary:
1692             strcat(fileName, "$(A)");
1693             break;
1694       }
1695    }
1696
1697    bool GenerateDebugCf(CompilerConfig compiler)
1698    {
1699       bool result = false;
1700       char path[MAX_LOCATION];
1701
1702       GetCompilerConfigsDir(path);
1703       if(!FileExists(path).isDirectory)
1704          MakeDir(path);
1705       PathCatSlash(path, "debug.cf");
1706
1707       if(FileExists(path))
1708          DeleteFile(path);
1709       {
1710          File f = FileOpen(path, write);
1711          if(f)
1712          {
1713             f.Printf(".PHONY: debug_lists\n");
1714             f.Printf("\n");
1715
1716             f.Printf("ifdef WINDOWS\n");
1717             f.Printf(".PHONY: debug_openssl\n");
1718             f.Printf("debug_openssl:\n");
1719             f.Printf("  @$(call echo,OPENSSL_CONF = $(OPENSSL_CONF))\n");
1720             f.Printf("  @$(call echo,_OPENSSL_CONF = $(_OPENSSL_CONF))\n");
1721             f.Printf("  @$(call echo,OPENSSL_INCLUDE_DIR = $(OPENSSL_INCLUDE_DIR))\n");
1722             f.Printf("  @$(call echo,OPENSSL_LIB_DIR = $(OPENSSL_LIB_DIR))\n");
1723             f.Printf("  @$(call echo,OPENSSL_BIN_DIR = $(OPENSSL_BIN_DIR))\n");
1724             f.Printf("endif\n");
1725             f.Printf("\n");
1726
1727             f.Printf("debug_lists:\n");
1728             f.Printf("  @$(call echo,ECSOURCES = $(ECSOURCES))\n");
1729             f.Printf("  @$(call echo,SYMBOLS = $(SYMBOLS))\n");
1730             f.Printf("  @$(call echo,IMPORTS = $(IMPORTS))\n");
1731             f.Printf("  @$(call echo,COBJECTS = $(COBJECTS))\n");
1732
1733             delete f;
1734          }
1735       }
1736       return result;
1737    }
1738
1739    bool GenerateCrossPlatformCf()
1740    {
1741       bool result = false;
1742       char path[MAX_LOCATION];
1743
1744       GetCompilerConfigsDir(path);
1745       if(!FileExists(path).isDirectory)
1746          MakeDir(path);
1747       PathCatSlash(path, "crossplatform.cf");
1748
1749       if(FileExists(path))
1750          DeleteFile(path);
1751       {
1752          File include = FileOpen(":crossplatform.cf", read);
1753          if(include)
1754          {
1755             File f = FileOpen(path, write);
1756             if(f)
1757             {
1758                for(; !include.Eof(); )
1759                {
1760                   char buffer[4096];
1761                   int count = include.Read(buffer, 1, 4096);
1762                   f.Write(buffer, 1, count);
1763                }
1764                delete f;
1765
1766                result = true;
1767             }
1768             delete include;
1769          }
1770       }
1771       return result;
1772    }
1773
1774    bool GenerateCompilerMk(CompilerConfig compiler)
1775    {
1776       bool result = false;
1777       char path[MAX_LOCATION];
1778       char * name;
1779       char * compilerName;
1780       bool gccCompiler = compiler.ccCommand && strstr(compiler.ccCommand, "gcc") != null;
1781       Platform platform = GetRuntimePlatform();
1782
1783       compilerName = CopyString(compiler.name);
1784       CamelCase(compilerName);
1785       name = PrintString(platform, "-", compilerName, ".cf");
1786
1787       GetCompilerConfigsDir(path);
1788       if(!FileExists(path).isDirectory)
1789          MakeDir(path);
1790       PathCatSlash(path, name);
1791
1792       if(FileExists(path))
1793          DeleteFile(path);
1794       {
1795          File f = FileOpen(path, write);
1796          if(f)
1797          {
1798             bool crossCompiling = compiler.targetPlatform != platform;
1799
1800             f.Printf("# TOOLCHAIN\n\n");
1801
1802             //f.Printf("SHELL := %s\n", "ar"/*compiler.arCommand*/); // is this really needed?
1803             f.Printf("CPP := $(CCACHE_COMPILE) $(DISTCC_COMPILE) %s\n", compiler.cppCommand);
1804             f.Printf("CC := $(CCACHE_COMPILE) $(DISTCC_COMPILE) %s\n", compiler.ccCommand);
1805             f.Printf("ECP := %s\n", compiler.ecpCommand);
1806             f.Printf("ECC := %s\n", compiler.eccCommand);
1807             f.Printf("ECS := %s%s%s\n", compiler.ecsCommand,
1808                   crossCompiling ? " -t " : "", crossCompiling ? (char*)compiler.targetPlatform : "");
1809             f.Printf("EAR := %s\n", compiler.earCommand);
1810
1811             f.Printf("AS := as\n");
1812             f.Printf("LD := ld\n");
1813             f.Printf("AR := ar\n");
1814             f.Printf("STRIP := strip\n");
1815             f.Printf("UPX := upx\n");
1816
1817             f.Printf("\n");
1818
1819             f.Printf("UPXFLAGS = -9\n\n"); // TOFEAT: Compression Level Option? Other UPX Options?
1820
1821             f.Printf("# HARD CODED PLATFORM-SPECIFIC OPTIONS\n");
1822             f.Printf("ifdef %s\n", PlatformToMakefileVariable(tux));
1823             f.Printf("OFLAGS += -Wl,--no-undefined\n");
1824             f.Printf("endif\n\n");
1825
1826             // JF's
1827             f.Printf("ifdef %s\n", PlatformToMakefileVariable(apple));
1828             f.Printf("OFLAGS += -framework cocoa -framework OpenGL\n");
1829             f.Printf("endif\n\n");
1830
1831             if(crossCompiling)
1832                f.Printf("PLATFORM = %s\n", (char *)compiler.targetPlatform);
1833
1834             if((compiler.includeDirs && compiler.includeDirs.count) ||
1835                   (compiler.libraryDirs && compiler.libraryDirs.count))
1836             {
1837                if(compiler.includeDirs && compiler.includeDirs.count)
1838                {
1839                   f.Printf("CFLAGS +=");
1840                   OutputListOption(f, gccCompiler ? "isystem " : "I", compiler.includeDirs, lineEach, true);
1841                   f.Printf("\n");
1842                }
1843                if(compiler.libraryDirs && compiler.libraryDirs.count)
1844                {
1845                   f.Printf("OFLAGS +=");
1846                   OutputListOption(f, "L", compiler.libraryDirs, lineEach, true);
1847                   f.Printf("\n");
1848                }
1849                f.Printf("\n");
1850             }
1851
1852             delete f;
1853          }
1854       }
1855       delete name;
1856       delete compilerName;
1857       return result;
1858    }
1859
1860    bool GenerateMakefile(char * altMakefilePath, bool noResources, char * includemkPath,
1861       CompilerConfig compiler, ProjectConfig config)
1862    {
1863       bool result = false;
1864       char filePath[MAX_LOCATION];
1865       char makeFile[MAX_LOCATION];
1866       // PathBackup pathBackup { };
1867       // char oldDirectory[MAX_LOCATION];
1868       File f = null;
1869
1870       if(!altMakefilePath)
1871       {
1872          strcpy(filePath, topNode.path);
1873          CatMakeFileName(makeFile, config);
1874          PathCatSlash(filePath, makeFile);
1875       }
1876
1877 #if defined(__WIN32__) && !defined(MAKEFILE_GENERATOR)
1878       if(compiler.type.isVC)
1879       {
1880          GenerateVSSolutionFile(this, compiler);
1881          GenerateVCProjectFile(this, compiler);
1882       }
1883       else
1884 #endif
1885       f = FileOpen(altMakefilePath ? altMakefilePath : filePath, write);
1886
1887       /*SetPath(false, compiler, config);
1888       GetWorkingDir(oldDirectory, MAX_LOCATION);
1889       ChangeWorkingDir(topNode.path);*/
1890
1891       if(f)
1892       {
1893          bool test;
1894          int ifCount;
1895          Platform platform;
1896          Platform runtimePlatform = GetRuntimePlatform();
1897          char targetDir[MAX_LOCATION];
1898          char objDirExpNoSpaces[MAX_LOCATION];
1899          char objDirNoSpaces[MAX_LOCATION];
1900          char resDirNoSpaces[MAX_LOCATION];
1901          char targetDirExpNoSpaces[MAX_LOCATION];
1902          char fixedModuleName[MAX_FILENAME];
1903          char fixedConfigName[MAX_FILENAME];
1904          char fixedCompilerName[MAX_FILENAME];
1905          int c, len;
1906          int numCObjects = 0;
1907          bool sameObjTargetDirs;
1908          DirExpression objDirExp = GetObjDir(compiler, config);
1909          TargetTypes targetType = GetTargetType(config);
1910
1911          bool crossCompiling = compiler.targetPlatform != runtimePlatform;
1912          bool gccCompiler = compiler.ccCommand && strstr(compiler.ccCommand, "gcc") != null;
1913          bool tccCompiler = compiler.ccCommand && strstr(compiler.ccCommand, "tcc") != null;
1914          bool defaultPreprocessor = compiler.cppCommand && (strstr(compiler.cppCommand, "gcc") != null || compiler.cppCommand && strstr(compiler.cppCommand, "cpp") != null);
1915
1916          int objectsParts, eCsourcesParts;
1917          Array<String> listItems { };
1918          Map<String, int> varStringLenDiffs { };
1919          Map<String, NameCollisionInfo> namesInfo { };
1920
1921          ReplaceSpaces(objDirNoSpaces, objDirExp.dir);
1922          strcpy(targetDir, GetTargetDirExpression(compiler, config));
1923          ReplaceSpaces(targetDirExpNoSpaces, targetDir);
1924
1925          strcpy(objDirExpNoSpaces, GetObjDirExpression(compiler, config));
1926          ChangeCh(objDirExpNoSpaces, '\\', '/'); // TODO: this is a hack, paths should never include win32 path seperators - fix this in ProjectSettings and ProjectLoad instead
1927          ReplaceSpaces(objDirExpNoSpaces, objDirExpNoSpaces);
1928          ReplaceSpaces(resDirNoSpaces, resNode.path ? resNode.path : "");
1929          //ReplaceSpaces(fixedPrjName, name);
1930          ReplaceSpaces(fixedModuleName, moduleName);
1931          ReplaceSpaces(fixedConfigName, GetConfigName(config));
1932          ReplaceSpaces(fixedCompilerName, compiler.name);
1933          //CamelCase(fixedModuleName); // case is important for static linking
1934          CamelCase(fixedConfigName);
1935          CamelCase(fixedCompilerName);
1936
1937          sameObjTargetDirs = !fstrcmp(objDirExpNoSpaces, targetDirExpNoSpaces);
1938
1939          f.Printf(".PHONY: all objdir%s clean realclean\n\n", sameObjTargetDirs ? "" : " targetdir");
1940
1941          f.Printf("# CONTENT\n\n");
1942
1943          f.Printf("MODULE := %s\n", fixedModuleName);
1944          //f.Printf("VERSION = %s\n", version);
1945          f.Printf("CONFIG := %s\n", fixedConfigName);
1946          f.Printf("ifndef COMPILER\n");
1947          f.Printf("COMPILER := %s\n", fixedCompilerName);
1948          f.Printf("endif\n");
1949          test = GetTargetTypeIsSetByPlatform(config);
1950          if(test)
1951          {
1952             ifCount = 0;
1953             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
1954             {
1955                TargetTypes targetType;
1956                PlatformOptions projectPOs, configPOs;
1957                MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
1958                targetType = platformTargetType;
1959                if(targetType)
1960                {
1961                   if(ifCount)
1962                      f.Printf("else\n");
1963                   ifCount++;
1964                   f.Printf("ifdef %s\n", PlatformToMakefileVariable(platform));
1965
1966                   f.Printf("TARGET_TYPE = ");
1967                   f.Printf(TargetTypeToMakefileVariable(targetType));
1968                   f.Printf("\n");
1969                }
1970             }
1971             f.Printf("else\n"); // ifCount should always be > 0
1972          }
1973          f.Printf("TARGET_TYPE = ");
1974          f.Printf(TargetTypeToMakefileVariable(targetType));
1975          f.Printf("\n");
1976          if(test)
1977          {
1978             if(ifCount)
1979             {
1980                for(c = 0; c < ifCount; c++)
1981                   f.Printf("endif\n");
1982             }
1983          }
1984          f.Printf("\n");
1985
1986          f.Printf("# FLAGS\n\n");
1987
1988          f.Printf("CFLAGS =\n");
1989          f.Printf("CECFLAGS =\n");
1990          f.Printf("ECFLAGS =\n");
1991          f.Printf("OFLAGS =\n");
1992          f.Printf("LIBS =\n");
1993          f.Printf("\n");
1994
1995          f.Printf("# INCLUDES\n\n");
1996
1997          f.Printf("include %s\n", includemkPath ? includemkPath : "$(E_IDE_CF_DIR)crossplatform.cf");
1998          f.Printf("include $(E_IDE_CF_DIR)%s-%s.cf\n", (char*)runtimePlatform, fixedCompilerName);
1999          f.Printf("\n");
2000
2001          f.Printf("# VARIABLES\n\n");
2002
2003          f.Printf("OBJ = %s%s\n\n", objDirExpNoSpaces, objDirExpNoSpaces[0] ? "/" : "");
2004
2005          f.Printf("RES = %s%s\n\n", resDirNoSpaces, resDirNoSpaces[0] ? "/" : "");
2006
2007          // test = GetTargetTypeIsSetByPlatform(config);
2008          {
2009             char target[MAX_LOCATION];
2010             char targetNoSpaces[MAX_LOCATION];
2011          if(test)
2012          {
2013             TargetTypes type;
2014             ifCount = 0;
2015             for(type = (TargetTypes)1; type < TargetTypes::enumSize; type++)
2016             {
2017                if(type != targetType)
2018                {
2019                   if(ifCount)
2020                      f.Printf("else\n");
2021                   ifCount++;
2022                   f.Printf("ifeq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(type));
2023
2024                   GetMakefileTargetFileName(type, target, config);
2025                   strcpy(targetNoSpaces, targetDir);
2026                   PathCatSlash(targetNoSpaces, target);
2027                   ReplaceSpaces(targetNoSpaces, targetNoSpaces);
2028                   f.Printf("TARGET = %s\n", targetNoSpaces);
2029                }
2030             }
2031             f.Printf("else\n"); // ifCount should always be > 0
2032          }
2033          GetMakefileTargetFileName(targetType, target, config);
2034          strcpy(targetNoSpaces, targetDir);
2035          PathCatSlash(targetNoSpaces, target);
2036          ReplaceSpaces(targetNoSpaces, targetNoSpaces);
2037          f.Printf("TARGET = %s\n", targetNoSpaces);
2038          if(test)
2039          {
2040             if(ifCount)
2041             {
2042                for(c = 0; c < ifCount; c++)
2043                   f.Printf("endif\n");
2044             }
2045          }
2046          }
2047          f.Printf("\n");
2048
2049          varStringLenDiffs["$(OBJ)"] = strlen(objDirNoSpaces) - 6;
2050
2051          topNode.GenMakefileGetNameCollisionInfo(namesInfo, config);
2052
2053          numCObjects = topNode.GenMakefilePrintNode(f, this, objects, namesInfo, listItems, config);
2054          if(numCObjects)
2055             listItems.Add(CopyString("$(OBJ)$(MODULE).main$(O)"));
2056          objectsParts = OutputFileList(f, "OBJECTS", listItems, varStringLenDiffs, null);
2057
2058          {
2059             int c;
2060             char * map[3][2] = { { "COBJECTS", "C" }, { "SYMBOLS", "S" }, { "IMPORTS", "I" } };
2061
2062             topNode.GenMakefilePrintNode(f, this, eCsources, namesInfo, listItems, config);
2063             eCsourcesParts = OutputFileList(f, "_ECSOURCES", listItems, varStringLenDiffs, null);
2064
2065             f.Printf("ECSOURCES = $(call shwspace,$(_ECSOURCES))\n");
2066             if(eCsourcesParts > 1)
2067             {
2068                for(c = 1; c <= eCsourcesParts; c++)
2069                   f.Printf("ECSOURCES%d = $(call shwspace,$(_ECSOURCES%d))\n", c, c);
2070             }
2071             f.Printf("\n");
2072
2073             for(c = 0; c < 3; c++)
2074             {
2075                if(eCsourcesParts > 1)
2076                {
2077                   int n;
2078                   f.Printf("%s =", map[c][0]);
2079                   for(n = 1; n <= eCsourcesParts; n++)
2080                      f.Printf(" $(%s%d)", map[c][0], n);
2081                   f.Printf("\n");
2082                   for(n = 1; n <= eCsourcesParts; n++)
2083                      f.Printf("%s%d = $(call shwspace,$(addprefix $(OBJ),$(patsubst %%.ec,%%$(%s),$(notdir $(_ECSOURCES%d)))))\n", map[c][0], n, map[c][1], n);
2084                }
2085                else if(eCsourcesParts == 1)
2086                   f.Printf("%s = $(call shwspace,$(addprefix $(OBJ),$(patsubst %%.ec,%%$(%s),$(notdir $(_ECSOURCES)))))\n", map[c][0], map[c][1]);
2087                f.Printf("\n");
2088             }
2089          }
2090
2091          topNode.GenMakefilePrintNode(f, this, sources, null, listItems, config);
2092          OutputFileList(f, "SOURCES", listItems, varStringLenDiffs, "$(ECSOURCES)");
2093
2094          if(!noResources)
2095             resNode.GenMakefilePrintNode(f, this, resources, null, listItems, config);
2096          OutputFileList(f, "RESOURCES", listItems, varStringLenDiffs, null);
2097
2098          f.Printf("ifeq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(executable));
2099          f.Printf("CONSOLE = %s\n", GetConsole(config) ? "-mconsole" : "-mwindows");
2100          f.Printf("endif\n\n");
2101
2102          if((config && config.options && config.options.libraries) ||
2103                (options && options.libraries))
2104          {
2105             f.Printf("ifneq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(staticLibrary));
2106             f.Printf("LIBS +=");
2107             if(config && config.options && config.options.libraries)
2108                OutputLibraries(f, config.options.libraries);
2109             else if(options && options.libraries)
2110                OutputLibraries(f, options.libraries);
2111             f.Printf("\n");
2112             f.Printf("endif\n");
2113             f.Printf("\n");
2114          }
2115          f.Printf("LIBS += $(SHAREDLIB) $(EXECUTABLE) $(LINKOPT)\n\n");
2116
2117          if(platforms || (config && config.platforms))
2118          {
2119             ifCount = 0;
2120             //for(platform = firstPlatform; platform <= lastPlatform; platform++)
2121             //for(platform = win32; platform <= apple; platform++)
2122
2123             f.Printf("# PLATFORM-SPECIFIC OPTIONS\n\n");
2124             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
2125             {
2126                PlatformOptions projectPlatformOptions, configPlatformOptions;
2127                MatchProjectAndConfigPlatformOptions(config, platform, &projectPlatformOptions, &configPlatformOptions);
2128
2129                if(projectPlatformOptions || configPlatformOptions)
2130                {
2131                   if(ifCount)
2132                      f.Printf("else\n");
2133                   ifCount++;
2134                   f.Printf("ifdef ");
2135                   f.Printf(PlatformToMakefileVariable(platform));
2136                   f.Printf("\n\n");
2137
2138                   if((projectPlatformOptions && projectPlatformOptions.options.preprocessorDefinitions && projectPlatformOptions.options.preprocessorDefinitions.count) ||
2139                      (configPlatformOptions && configPlatformOptions.options.preprocessorDefinitions && configPlatformOptions.options.preprocessorDefinitions.count) ||
2140                      (projectPlatformOptions && projectPlatformOptions.options.includeDirs && projectPlatformOptions.options.includeDirs.count) ||
2141                      (configPlatformOptions && configPlatformOptions.options.includeDirs && configPlatformOptions.options.includeDirs.count))
2142                   {
2143                      f.Printf("CFLAGS +=");
2144                      if(projectPlatformOptions && projectPlatformOptions.options.linkerOptions && projectPlatformOptions.options.linkerOptions.count)
2145                      {
2146                         f.Printf(" \\\n\t -Wl");
2147                         for(s : projectPlatformOptions.options.linkerOptions)
2148                            f.Printf(",%s", s);
2149                      }
2150                      if(configPlatformOptions && configPlatformOptions.options.linkerOptions && configPlatformOptions.options.linkerOptions.count)
2151                      {
2152                         f.Printf(" \\\n\t -Wl");
2153                         for(s : configPlatformOptions.options.linkerOptions)
2154                            f.Printf(",%s", s);
2155                      }
2156                      if(projectPlatformOptions && projectPlatformOptions.options.preprocessorDefinitions)
2157                         OutputListOption(f, "D", projectPlatformOptions.options.preprocessorDefinitions, newLine, false);
2158                      if(configPlatformOptions && configPlatformOptions.options.preprocessorDefinitions)
2159                         OutputListOption(f, "D", configPlatformOptions.options.preprocessorDefinitions, newLine, false );
2160                      if(configPlatformOptions && configPlatformOptions.options.includeDirs)
2161                         OutputListOption(f, "I", configPlatformOptions.options.includeDirs, lineEach, true);
2162                      if(projectPlatformOptions && projectPlatformOptions.options.includeDirs)
2163                         OutputListOption(f, "I", projectPlatformOptions.options.includeDirs, lineEach, true);
2164                      f.Printf("\n\n");
2165                   }
2166
2167                   if((projectPlatformOptions && projectPlatformOptions.options.libraryDirs && projectPlatformOptions.options.libraryDirs.count) ||
2168                         (configPlatformOptions && configPlatformOptions.options.libraryDirs && configPlatformOptions.options.libraryDirs.count) ||
2169                         (projectPlatformOptions && projectPlatformOptions.options.libraries && projectPlatformOptions.options.libraries.count) ||
2170                         (configPlatformOptions && configPlatformOptions.options.libraries && configPlatformOptions.options.libraries.count))
2171                   {
2172                      f.Printf("ifneq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(staticLibrary));
2173                      if((projectPlatformOptions && projectPlatformOptions.options.libraryDirs && projectPlatformOptions.options.libraryDirs.count) ||
2174                         (configPlatformOptions && configPlatformOptions.options.libraryDirs && configPlatformOptions.options.libraryDirs.count))
2175                      {
2176                         f.Printf("OFLAGS +=");
2177                         if(configPlatformOptions && configPlatformOptions.options.libraryDirs)
2178                            OutputListOption(f, "L", configPlatformOptions.options.libraryDirs, lineEach, true);
2179                         if(projectPlatformOptions && projectPlatformOptions.options.libraryDirs)
2180                            OutputListOption(f, "L", projectPlatformOptions.options.libraryDirs, lineEach, true);
2181                         f.Printf("\n");
2182                      }
2183
2184                      if(projectPlatformOptions && projectPlatformOptions.options.libraries &&
2185                            projectPlatformOptions.options.libraries.count)
2186                      {
2187                         f.Printf("LIBS +=");
2188                         OutputLibraries(f, projectPlatformOptions.options.libraries);
2189                         f.Printf("\n");
2190                      }
2191                      if((configPlatformOptions && configPlatformOptions.options.libraries &&
2192                            configPlatformOptions.options.libraries.count))
2193                      {
2194                         f.Printf("LIBS +=");
2195                         OutputLibraries(f, configPlatformOptions.options.libraries);
2196                         f.Printf("\n");
2197                      }
2198                      f.Printf("endif\n\n");
2199                   }
2200                }
2201             }
2202             if(ifCount)
2203             {
2204                for(c = 0; c < ifCount; c++)
2205                   f.Printf("endif\n");
2206             }
2207             f.Printf("\n");
2208          }
2209
2210          f.Printf("CFLAGS +=");
2211          if(gccCompiler)
2212          {
2213             f.Printf(" -fmessage-length=0");
2214             switch(GetOptimization(config))
2215             {
2216                case speed:
2217                   f.Printf(" -O2");
2218                   f.Printf(" -ffast-math");
2219                   break;
2220                case size:
2221                   f.Printf(" -Os");
2222                   break;
2223             }
2224             //if(compiler.targetPlatform.is32Bits)
2225                f.Printf(" -m32");
2226             //else if(compiler.targetPlatform.is64Bits)
2227             //   f.Printf(" -m64");
2228             f.Printf(" $(FPIC)");
2229             //f.Printf(" -fpack-struct");
2230          }
2231          switch(GetWarnings(config))
2232          {
2233             case all: f.Printf(" -Wall"); break;
2234             case none: f.Printf(" -w"); break;
2235          }
2236          if(GetDebug(config))
2237             f.Printf(" -g");
2238          if(GetProfile(config))
2239             f.Printf(" -pg");
2240          if(options && options.linkerOptions && options.linkerOptions.count)
2241          {
2242             f.Printf(" \\\n\t -Wl");
2243             for(s : options.linkerOptions)
2244                f.Printf(",%s", s);
2245          }
2246
2247          if(options && options.preprocessorDefinitions)
2248             OutputListOption(f, "D", options.preprocessorDefinitions, newLine, false);
2249          if(config && config.options && config.options.preprocessorDefinitions)
2250             OutputListOption(f, "D", config.options.preprocessorDefinitions, newLine, false);
2251          if(config && config.options && config.options.includeDirs)
2252             OutputListOption(f, "I", config.options.includeDirs, lineEach, true);
2253          if(options && options.includeDirs)
2254             OutputListOption(f, "I", options.includeDirs, lineEach, true);
2255          f.Printf("\n\n");
2256
2257          f.Printf("CECFLAGS +=%s%s%s%s", defaultPreprocessor ? "" : " -cpp ", defaultPreprocessor ? "" : compiler.cppCommand,
2258                crossCompiling ? " -t " : "", crossCompiling ? (char*)compiler.targetPlatform : "");
2259          f.Printf("\n\n");
2260
2261          f.Printf("ECFLAGS +=");
2262          if(GetMemoryGuard(config))
2263             f.Printf(" -memguard");
2264          if(GetStrictNameSpaces(config))
2265             f.Printf(" -strictns");
2266          if(GetNoLineNumbers(config))
2267             f.Printf(" -nolinenumbers");
2268          {
2269             char * s;
2270             if((s = GetDefaultNameSpace(config)) && s[0])
2271                f.Printf(" -defaultns %s", s);
2272          }
2273          f.Printf("\n\n");
2274
2275          f.Printf("OFLAGS +=\n");
2276          f.Printf("ifneq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(staticLibrary));
2277          f.Printf("OFLAGS += -m32");
2278          if(GetProfile(config))
2279             f.Printf(" -pg");
2280          if(config && config.options && config.options.libraryDirs)
2281             OutputListOption(f, "L", config.options.libraryDirs, lineEach, true);
2282          if(options && options.libraryDirs)
2283             OutputListOption(f, "L", options.libraryDirs, lineEach, true);
2284          f.Printf("\n");
2285          f.Printf("endif\n\n");
2286
2287          f.Printf("# TARGETS\n\n");
2288
2289          f.Printf("all: objdir%s $(TARGET)\n\n", sameObjTargetDirs ? "" : " targetdir");
2290
2291          f.Printf("objdir:\n");
2292             f.Printf("\t$(if $(wildcard $(OBJ)),,$(call mkdirq,$(OBJ)))\n");
2293          //f.Printf("# PRE-BUILD COMMANDS\n");
2294          if(options && options.prebuildCommands)
2295          {
2296             for(s : options.prebuildCommands)
2297                if(s && s[0]) f.Printf("\t%s\n", s);
2298          }
2299          if(config && config.options && config.options.prebuildCommands)
2300          {
2301             for(s : config.options.prebuildCommands)
2302                if(s && s[0]) f.Printf("\t%s\n", s);
2303          }
2304          if(platforms || (config && config.platforms))
2305          {
2306             ifCount = 0;
2307             //f.Printf("# PLATFORM-SPECIFIC PRE-BUILD COMMANDS\n");
2308             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
2309             {
2310                PlatformOptions projectPOs, configPOs;
2311                MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
2312
2313                if((projectPOs && projectPOs.options.prebuildCommands && projectPOs.options.prebuildCommands.count) ||
2314                      (configPOs && configPOs.options.prebuildCommands && configPOs.options.prebuildCommands.count))
2315                {
2316                   if(ifCount)
2317                      f.Printf("else\n");
2318                   ifCount++;
2319                   f.Printf("ifdef ");
2320                   f.Printf(PlatformToMakefileVariable(platform));
2321                   f.Printf("\n");
2322
2323                   if(projectPOs && projectPOs.options.prebuildCommands && projectPOs.options.prebuildCommands.count)
2324                   {
2325                      for(s : projectPOs.options.prebuildCommands)
2326                         if(s && s[0]) f.Printf("\t%s\n", s);
2327                   }
2328                   if(configPOs && configPOs.options.prebuildCommands && configPOs.options.prebuildCommands.count)
2329                   {
2330                      for(s : configPOs.options.prebuildCommands)
2331                         if(s && s[0]) f.Printf("\t%s\n", s);
2332                   }
2333                }
2334             }
2335             if(ifCount)
2336             {
2337                int c;
2338                for(c = 0; c < ifCount; c++)
2339                   f.Printf("endif\n");
2340             }
2341          }
2342          f.Printf("\n");
2343
2344          if(!sameObjTargetDirs)
2345          {
2346             f.Printf("targetdir:\n");
2347                f.Printf("\t$(if $(wildcard %s),,$(call mkdirq,%s))\n\n", targetDirExpNoSpaces, targetDirExpNoSpaces);
2348          }
2349
2350          if(numCObjects)
2351          {
2352             // Main Module (Linking) for ECERE C modules
2353             f.Printf("$(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS)\n");
2354             // use of objDirExpNoSpaces used instead of $(OBJ) to prevent problematic joining of arguments in ecs
2355             f.Printf("\t$(ECS)%s $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols %s -o $(OBJ)$(MODULE).main.ec\n\n", 
2356                GetConsole(config) ? " -console" : "", objDirExpNoSpaces);
2357             // Main Module (Linking) for ECERE C modules
2358             f.Printf("$(OBJ)$(MODULE).main.c: $(OBJ)$(MODULE).main.ec\n");
2359             f.Printf("\t$(ECP) $(CECFLAGS) $(ECFLAGS) $(CFLAGS)"
2360                   " -c $(OBJ)$(MODULE).main.ec -o $(OBJ)$(MODULE).main.sym -symbols $(OBJ)\n");
2361             f.Printf("\t$(ECC) $(CECFLAGS) $(ECFLAGS) $(CFLAGS) $(FVISIBILITY)"
2362                   " -c $(OBJ)$(MODULE).main.ec -o $(OBJ)$(MODULE).main.c -symbols $(OBJ)\n\n");
2363          }
2364
2365          // *** Target ***
2366
2367          // This would not rebuild the target on updated objects
2368          // f.Printf("$(TARGET): $(SOURCES) $(RESOURCES) | objdir $(SYMBOLS) $(OBJECTS)%s\n", sameObjTargetDirs ? "" : " targetdir");
2369
2370          // This should fix it for good!
2371          f.Printf("$(SYMBOLS): | objdir\n");
2372          f.Printf("$(OBJECTS): | objdir\n");
2373
2374          // This alone was breaking the tarball, object directory does not get created first (order-only rules happen last it seems!)
2375          f.Printf("$(TARGET): $(SOURCES) $(RESOURCES) $(SYMBOLS) $(OBJECTS) | objdir%s\n", sameObjTargetDirs ? "" : " targetdir");
2376
2377          f.Printf("ifneq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(staticLibrary));
2378          f.Printf("\t$(CC) $(OFLAGS) $(OBJECTS) $(LIBS) -o $(TARGET) $(INSTALLNAME)\n");
2379          if(!GetDebug(config))
2380          {
2381             f.Printf("ifndef NOSTRIP\n");
2382             f.Printf("\t$(STRIP) $(STRIPOPT) $(TARGET)\n");
2383             f.Printf("endif\n");
2384
2385             if(GetCompress(config))
2386             {
2387                f.Printf("ifndef WINDOWS\n");
2388                f.Printf("ifeq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(executable));
2389                   f.Printf("\t$(UPX) $(UPXFLAGS) $(TARGET)\n");
2390                f.Printf("endif\n");
2391                f.Printf("else\n");
2392                   f.Printf("\t$(UPX) $(UPXFLAGS) $(TARGET)\n");
2393                f.Printf("endif\n");
2394             }
2395          }
2396          if(resNode.files && resNode.files.count && !noResources)
2397             resNode.GenMakefileAddResources(f, resNode.path, config);
2398          f.Printf("else\n");
2399          f.Printf("\t$(AR) rcs $(TARGET) $(OBJECTS) $(LIBS)\n");
2400          f.Printf("endif\n");
2401
2402          //f.Printf("# POST-BUILD COMMANDS\n");
2403          if(options && options.postbuildCommands)
2404          {
2405             for(s : options.postbuildCommands)
2406                if(s && s[0]) f.Printf("\t%s\n", s);
2407          }
2408          if(config && config.options && config.options.postbuildCommands)
2409          {
2410             for(s : config.options.postbuildCommands)
2411                if(s && s[0]) f.Printf("\t%s\n", s);
2412          }
2413          if(platforms || (config && config.platforms))
2414          {
2415             ifCount = 0;
2416             //f.Printf("# PLATFORM-SPECIFIC POST-BUILD COMMANDS\n");
2417             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
2418             {
2419                PlatformOptions projectPOs, configPOs;
2420                MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
2421
2422                if((projectPOs && projectPOs.options.postbuildCommands && projectPOs.options.postbuildCommands.count) ||
2423                      (configPOs && configPOs.options.postbuildCommands && configPOs.options.postbuildCommands.count))
2424                {
2425                   if(ifCount)
2426                      f.Printf("else\n");
2427                   ifCount++;
2428                   f.Printf("ifdef ");
2429                   f.Printf(PlatformToMakefileVariable(platform));
2430                   f.Printf("\n");
2431
2432                   if(projectPOs && projectPOs.options.postbuildCommands && projectPOs.options.postbuildCommands.count)
2433                   {
2434                      for(s : projectPOs.options.postbuildCommands)
2435                         if(s && s[0]) f.Printf("\t%s\n", s);
2436                   }
2437                   if(configPOs && configPOs.options.postbuildCommands && configPOs.options.postbuildCommands.count)
2438                   {
2439                      for(s : configPOs.options.postbuildCommands)
2440                         if(s && s[0]) f.Printf("\t%s\n", s);
2441                   }
2442                }
2443             }
2444             if(ifCount)
2445             {
2446                int c;
2447                for(c = 0; c < ifCount; c++)
2448                   f.Printf("endif\n");
2449             }
2450          }
2451          f.Printf("\n");
2452
2453          f.Printf("# SYMBOL RULES\n\n");
2454          {
2455             Map<Platform, bool> excludedPlatforms { };
2456             topNode.GenMakefilePrintSymbolRules(f, this, compiler, config, excludedPlatforms);
2457             delete excludedPlatforms;
2458          }
2459
2460          f.Printf("# C OBJECT RULES\n\n");
2461          {
2462             Map<Platform, bool> excludedPlatforms { };
2463             topNode.GenMakefilePrintCObjectRules(f, this, compiler, config, excludedPlatforms);
2464             delete excludedPlatforms;
2465          }
2466
2467          f.Printf("# OBJECT RULES\n\n");
2468          // todo call this still but only generate rules whith specific options
2469          // see we-have-file-specific-options in ProjectNode.ec
2470          {
2471             Map<Platform, bool> excludedPlatforms { };
2472             topNode.GenMakefilePrintObjectRules(f, this, namesInfo, compiler, config, excludedPlatforms);
2473             delete excludedPlatforms;
2474          }
2475
2476          if(numCObjects)
2477             GenMakefilePrintMainObjectRule(f, compiler, config);
2478
2479          f.Printf("clean: objdir%s\n", sameObjTargetDirs ? "" : " targetdir");
2480          f.Printf("\t$(call rmq,%s$(TARGET))\n", numCObjects ? "$(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S) " : "");
2481          OutputCleanActions(f, "OBJECTS", objectsParts);
2482          OutputCleanActions(f, "COBJECTS", eCsourcesParts);
2483          if(numCObjects)
2484          {
2485             OutputCleanActions(f, "IMPORTS", eCsourcesParts);
2486             OutputCleanActions(f, "SYMBOLS", eCsourcesParts);
2487          }
2488          f.Printf("\n");
2489
2490          f.Printf("realclean: clean\n");
2491          f.Printf("\t$(call rmrq,$(OBJ))\n");
2492          if(!sameObjTargetDirs)
2493             f.Printf("\t$(call rmdirq,%s)\n", targetDirExpNoSpaces);
2494          f.Printf("\n");
2495
2496          f.Printf("include $(E_IDE_CF_DIR)debug.cf\n");
2497
2498          delete f;
2499          delete objDirExp;
2500          listItems.Free();
2501          delete listItems;
2502          varStringLenDiffs.Free();
2503          delete varStringLenDiffs;
2504          namesInfo.Free();
2505          delete namesInfo;
2506
2507          result = true;
2508       }
2509
2510       // ChangeWorkingDir(oldDirectory);
2511       // delete pathBackup;
2512
2513       if(config)
2514          config.makingModified = false;
2515       return result;
2516    }
2517
2518    void GenMakefilePrintMainObjectRule(File f, CompilerConfig compiler, ProjectConfig config)
2519    {
2520       char extension[MAX_EXTENSION] = "c";
2521       char modulePath[MAX_LOCATION];
2522       char fixedModuleName[MAX_FILENAME];
2523       DualPipe dep;
2524       char command[2048];
2525       char objDirNoSpaces[MAX_LOCATION];
2526       DirExpression objDirExp = GetObjDir(compiler, config);
2527
2528       ReplaceSpaces(objDirNoSpaces, objDirExp.dir);
2529       ReplaceSpaces(fixedModuleName, moduleName);
2530       
2531       //sprintf(fixedModuleName, "%s.main", fixedPrjName);
2532       //strcat(fixedModuleName, ".main");
2533
2534 #if 0       // TODO: Fix nospaces stuff
2535       // *** Dependency command ***
2536       sprintf(command, "gcc -MT $(OBJ)%s$(O) -MM $(OBJ)%s.c", fixedModuleName, fixedModuleName);
2537
2538       // System Includes (from global settings)
2539       for(item : compiler.dirs[Includes])
2540       {
2541          strcat(command, " -isystem ");
2542          if(strchr(item.name, ' '))
2543          {
2544             strcat(command, "\"");
2545             strcat(command, item);
2546             strcat(command, "\"");
2547          }
2548          else
2549             strcat(command, item);
2550       }
2551
2552       for(item = includeDirs.first; item; item = item.next)
2553       {
2554          strcat(command, " -I");
2555          if(strchr(item.name, ' '))
2556          {
2557             strcat(command, "\"");
2558             strcat(command, item.name);
2559             strcat(command, "\"");
2560          }
2561          else
2562             strcat(command, item.name);
2563       }
2564       for(item = preprocessorDefs.first; item; item = item.next)
2565       {
2566          strcat(command, " -D");
2567          strcat(command, item.name);
2568       }
2569
2570       // Execute it
2571       if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
2572       {
2573          char line[1024];
2574          bool result = true;
2575          bool firstLine = true;
2576
2577          // To do some time: auto save external dependencies?
2578          while(!dep.Eof())
2579          {
2580             if(dep.GetLine(line, sizeof(line)-1))
2581             {
2582                if(firstLine)
2583                {
2584                   char * colon = strstr(line, ":");
2585                   if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
2586                   {
2587                      result = false;
2588                      break;
2589                   }
2590                   firstLine = false;
2591                }
2592                f.Puts(line);
2593                f.Puts("\n");
2594             }
2595             if(!result) break;
2596          }
2597          delete dep;
2598
2599          // If we failed to generate dependencies...
2600          if(!result)
2601          {
2602 #endif
2603             f.Printf("$(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c\n");
2604 #if 0
2605          }
2606       }
2607 #endif
2608
2609       f.Printf("\t$(CC) $(CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.%s -o $(OBJ)$(MODULE).main$(O)\n\n", extension);
2610
2611       delete objDirExp;
2612    }
2613
2614    void MatchProjectAndConfigPlatformOptions(ProjectConfig config, Platform platform,
2615          PlatformOptions * projectPlatformOptions, PlatformOptions * configPlatformOptions)
2616    {
2617       *projectPlatformOptions = null;
2618       *configPlatformOptions = null;
2619       if(platforms)
2620       {
2621          for(p : platforms)
2622          {
2623             if(!strcmpi(p.name, platform))
2624             {
2625                *projectPlatformOptions = p;
2626                break;
2627             }
2628          }
2629       }
2630       if(config && config.platforms)
2631       {
2632          for(p : config.platforms)
2633          {
2634             if(!strcmpi(p.name, platform))
2635             {
2636                *configPlatformOptions = p;
2637                break;
2638             }
2639          }
2640       }
2641    }
2642 }
2643
2644 Project LegacyBinaryLoadProject(File f, char * filePath)
2645 {
2646    Project project = null;
2647    char signature[sizeof(epjSignature)];
2648
2649    f.Read(signature, sizeof(signature), 1);
2650    if(!strncmp(signature, (char *)epjSignature, sizeof(epjSignature)))
2651    {
2652       char topNodePath[MAX_LOCATION];
2653       /*ProjectConfig newConfig
2654       {
2655          name = CopyString("Default");
2656          makingModified = true;
2657          compilingModified = true;
2658          linkingModified = true;
2659          options = { };
2660       };*/
2661
2662       project = Project { options = { } };
2663       LegacyBinaryLoadNode(project.topNode, f);
2664       delete project.topNode.path;
2665       GetWorkingDir(topNodePath, sizeof(topNodePath)-1);
2666       MakeSlashPath(topNodePath);
2667
2668       PathCatSlash(topNodePath, filePath);
2669       project.filePath = topNodePath;
2670       
2671       /* THIS IS ALREADY DONE BY filePath property
2672       StripLastDirectory(topNodePath, topNodePath);
2673       project.topNode.path = CopyString(topNodePath);
2674       */
2675       // Shouldn't this be done BEFORE the StripLastDirectory? project.filePath = topNodePath;
2676       
2677       // newConfig.options.defaultNameSpace = "";
2678       /*newConfig.objDir.dir = "obj";
2679       newConfig.targetDir.dir = "";*/
2680
2681       //project.configurations = { [ newConfig ] };
2682       //project.config = newConfig;
2683
2684       // Project Settings
2685       if(!f.Eof())
2686       {
2687          int temp;
2688          int len,c, count;
2689          String targetFileName, targetDirectory, objectsDirectory;
2690
2691          // { executable = 0, sharedLibrary = 1, staticLibrary = 2 };
2692          f.Read(&temp, sizeof(int),1);
2693          switch(temp)
2694          {
2695             case 0: project.options.targetType = executable; break;
2696             case 1: project.options.targetType = sharedLibrary; break;
2697             case 2: project.options.targetType = staticLibrary; break;
2698          }
2699
2700          f.Read(&len, sizeof(int),1);
2701          targetFileName = new char[len+1];
2702          f.Read(targetFileName, sizeof(char), len+1);
2703          project.options.targetFileName = targetFileName;
2704          delete targetFileName;
2705
2706          f.Read(&len, sizeof(int),1);
2707          targetDirectory = new char[len+1];
2708          f.Read(targetDirectory, sizeof(char), len+1);
2709          project.options.targetDirectory = targetDirectory;
2710          delete targetDirectory;
2711
2712          f.Read(&len, sizeof(int),1);
2713          objectsDirectory = new byte[len+1];
2714          f.Read(objectsDirectory, sizeof(char), len+1);
2715          project.options.objectsDirectory = objectsDirectory;
2716          delete objectsDirectory;
2717
2718          f.Read(&temp, sizeof(int),1);
2719          project./*config.*/options.debug = temp ? true : false;
2720          f.Read(&temp, sizeof(int),1);         
2721          project./*config.*/options.optimization = temp ? speed : none;
2722          f.Read(&temp, sizeof(int),1);
2723          project./*config.*/options.profile = temp ? true : false;
2724          f.Read(&temp, sizeof(int),1);
2725          project.options.warnings = temp ? all : unset;
2726
2727          f.Read(&count, sizeof(int),1);
2728          if(count)
2729          {
2730             project.options.includeDirs = { };
2731             for(c = 0; c < count; c++)
2732             {
2733                char * name;
2734                f.Read(&len, sizeof(int),1);
2735                name = new char[len+1];
2736                f.Read(name, sizeof(char), len+1);
2737                project.options.includeDirs.Add(name);
2738             }
2739          }
2740
2741          f.Read(&count, sizeof(int),1);
2742          if(count)
2743          {
2744             project.options.libraryDirs = { };
2745             for(c = 0; c < count; c++)
2746             {
2747                char * name;            
2748                f.Read(&len, sizeof(int),1);
2749                name = new char[len+1];
2750                f.Read(name, sizeof(char), len+1);
2751                project.options.libraryDirs.Add(name);
2752             }
2753          }
2754
2755          f.Read(&count, sizeof(int),1);
2756          if(count)
2757          {
2758             project.options.libraries = { };
2759             for(c = 0; c < count; c++)
2760             {
2761                char * name;
2762                f.Read(&len, sizeof(int),1);
2763                name = new char[len+1];
2764                f.Read(name, sizeof(char), len+1);
2765                project.options.libraries.Add(name);
2766             }
2767          }
2768
2769          f.Read(&count, sizeof(int),1);
2770          if(count)
2771          {
2772             project.options.preprocessorDefinitions = { };
2773             for(c = 0; c < count; c++)
2774             {
2775                char * name;
2776                f.Read(&len, sizeof(int),1);
2777                name = new char[len+1];
2778                f.Read(name, sizeof(char), len+1);
2779                project.options.preprocessorDefinitions.Add(name);
2780             }
2781          }
2782
2783          f.Read(&temp, sizeof(int),1);
2784          project.options.console = temp ? true : false;
2785       }
2786
2787       for(node : project.topNode.files)
2788       {
2789          if(node.type == resources)
2790          {
2791             project.resNode = node;
2792             break;
2793          }
2794       }
2795    }
2796    else
2797       f.Seek(0, start);
2798    return project;
2799 }
2800
2801 void ProjectConfig::LegacyProjectConfigLoad(File f)
2802 {  
2803    delete options;
2804    options = { };
2805    while(!f.Eof())
2806    {
2807       char buffer[65536];
2808       char section[128];
2809       char subSection[128];
2810       char * equal;
2811       int len;
2812       uint pos;
2813       
2814       pos = f.Tell();
2815       f.GetLine(buffer, 65536 - 1);
2816       TrimLSpaces(buffer, buffer);
2817       TrimRSpaces(buffer, buffer);
2818       if(strlen(buffer))
2819       {
2820          if(buffer[0] == '-')
2821          {
2822             equal = &buffer[0];
2823             equal[0] = ' ';
2824             TrimLSpaces(equal, equal);
2825             if(!strcmpi(subSection, "LibraryDirs"))
2826             {
2827                if(!options.libraryDirs)
2828                   options.libraryDirs = { [ CopyString(equal) ] };
2829                else
2830                   options.libraryDirs.Add(CopyString(equal));
2831             }
2832             else if(!strcmpi(subSection, "IncludeDirs"))
2833             {
2834                if(!options.includeDirs)
2835                   options.includeDirs = { [ CopyString(equal) ] };
2836                else
2837                   options.includeDirs.Add(CopyString(equal));
2838             }
2839          }
2840          else if(buffer[0] == '+')
2841          {
2842             if(name)
2843             {
2844                f.Seek(pos, start);
2845                break;
2846             }
2847             else
2848             {
2849                equal = &buffer[0];
2850                equal[0] = ' ';
2851                TrimLSpaces(equal, equal);
2852                delete name; name = CopyString(equal); // property::name = equal;
2853             }
2854          }
2855          else if(!strcmpi(buffer, "Compiler Options"))
2856             strcpy(section, buffer);
2857          else if(!strcmpi(buffer, "IncludeDirs"))
2858             strcpy(subSection, buffer);
2859          else if(!strcmpi(buffer, "Linker Options"))
2860             strcpy(section, buffer);
2861          else if(!strcmpi(buffer, "LibraryDirs"))
2862             strcpy(subSection, buffer);
2863          else if(!strcmpi(buffer, "Files") || !strcmpi(buffer, "Resources"))
2864          {
2865             f.Seek(pos, start);
2866             break;
2867          }
2868          else
2869          {
2870             equal = strstr(buffer, "=");
2871             if(equal)
2872             {
2873                equal[0] = '\0';
2874                TrimRSpaces(buffer, buffer);
2875                equal++;
2876                TrimLSpaces(equal, equal);
2877                if(!strcmpi(buffer, "Target Name"))
2878                   options.targetFileName = /*CopyString(*/equal/*)*/;
2879                else if(!strcmpi(buffer, "Target Type"))
2880                {
2881                   if(!strcmpi(equal, "Executable"))
2882                      options.targetType = executable;
2883                   else if(!strcmpi(equal, "Shared"))
2884                      options.targetType = sharedLibrary;
2885                   else if(!strcmpi(equal, "Static"))
2886                      options.targetType = staticLibrary;
2887                   else
2888                      options.targetType = executable;
2889                }
2890                else if(!strcmpi(buffer, "Target Directory"))
2891                   options.targetDirectory = /*CopyString(*/equal/*)*/;
2892                else if(!strcmpi(buffer, "Console"))
2893                   options.console = ParseTrueFalseValue(equal);
2894                else if(!strcmpi(buffer, "Libraries"))
2895                {
2896                   if(!options.libraries) options.libraries = { };
2897                   ParseArrayValue(options.libraries, equal);
2898                }
2899                else if(!strcmpi(buffer, "Intermediate Directory"))
2900                   options.objectsDirectory = /*CopyString(*/equal/*)*/; //objDir.expression = equal;
2901                else if(!strcmpi(buffer, "Debug"))
2902                   options.debug = ParseTrueFalseValue(equal);
2903                else if(!strcmpi(buffer, "Optimize"))
2904                {
2905                   if(!strcmpi(equal, "None"))
2906                      options.optimization = none;
2907                   else if(!strcmpi(equal, "Speed") || !strcmpi(equal, "True"))
2908                      options.optimization = speed;
2909                   else if(!strcmpi(equal, "Size"))
2910                      options.optimization = size;
2911                   else
2912                      options.optimization = none;
2913                }
2914                else if(!strcmpi(buffer, "Compress"))
2915                   options.compress = ParseTrueFalseValue(equal);
2916                else if(!strcmpi(buffer, "Profile"))
2917                   options.profile = ParseTrueFalseValue(equal);
2918                else if(!strcmpi(buffer, "AllWarnings"))
2919                   options.warnings = ParseTrueFalseValue(equal) ? all : unset;
2920                else if(!strcmpi(buffer, "MemoryGuard"))
2921                   options.memoryGuard = ParseTrueFalseValue(equal);
2922                else if(!strcmpi(buffer, "Default Name Space"))
2923                   options.defaultNameSpace = CopyString(equal);
2924                else if(!strcmpi(buffer, "Strict Name Spaces"))
2925                   options.strictNameSpaces = ParseTrueFalseValue(equal);
2926                else if(!strcmpi(buffer, "Preprocessor Definitions"))
2927                {
2928                   if(!options.preprocessorDefinitions) options.preprocessorDefinitions = { };
2929                   ParseArrayValue(options.preprocessorDefinitions, equal);
2930                }
2931             }
2932          }
2933       }
2934    }
2935    if(!options.targetDirectory && options.objectsDirectory)
2936       options.targetDirectory = /*CopyString(*/options.objectsDirectory/*)*/;
2937    //if(!objDir.dir) objDir.dir = "obj";
2938    //if(!targetDir.dir) targetDir.dir = "";
2939    // if(!targetName) property::targetName = "";   // How can a targetFileName be nothing???
2940    // if(!defaultNameSpace) property::defaultNameSpace = "";
2941    makingModified = true;
2942 }
2943
2944 Project LegacyAsciiLoadProject(File f, char * filePath)
2945 {
2946    Project project = null;
2947    ProjectNode node = null;
2948    int pos;
2949    char parentPath[MAX_LOCATION];
2950    char section[128] = "";
2951    char subSection[128] = "";
2952    ProjectNode parent;
2953    bool configurationsPresent = false;
2954
2955    f.Seek(0, start);
2956    while(!f.Eof())
2957    {
2958       char buffer[65536];
2959       //char version[16];
2960       char * equal;
2961       int len;
2962       pos = f.Tell();
2963       f.GetLine(buffer, 65536 - 1);
2964       TrimLSpaces(buffer, buffer);
2965       TrimRSpaces(buffer, buffer);
2966       if(strlen(buffer))
2967       {
2968          if(buffer[0] == '-' || buffer[0] == '=')
2969          {
2970             bool simple = buffer[0] == '-';
2971             equal = &buffer[0];
2972             equal[0] = ' ';
2973             TrimLSpaces(equal, equal);
2974             if(!strcmpi(section, "Target") && !strcmpi(subSection, "LibraryDirs"))
2975             {
2976                if(!project.config.options.libraryDirs) project.config.options.libraryDirs = { };
2977                project.config.options.libraryDirs.Add(CopyString(equal));
2978             }
2979             else if(!strcmpi(section, "Target") && !strcmpi(subSection, "IncludeDirs"))
2980             {
2981                if(!project.config.options.includeDirs) project.config.options.includeDirs = { };
2982                project.config.options.includeDirs.Add(CopyString(equal));
2983             }
2984             else if(!strcmpi(section, "Target") && (!strcmpi(subSection, "Files") || !strcmpi(subSection, "Resources")))
2985             {
2986                len = strlen(equal);
2987                if(len)
2988                {
2989                   char temp[MAX_LOCATION];
2990                   ProjectNode child { };
2991                   // We don't need to do this anymore, fileName is just a property that sets name & path
2992                   // child.fileName = CopyString(equal);
2993                   if(simple)
2994                   {
2995                      child.name = CopyString(equal);
2996                      child.path = CopyString(parentPath);
2997                   }
2998                   else
2999                   {
3000                      GetLastDirectory(equal, temp);
3001                      child.name = CopyString(temp);
3002                      StripLastDirectory(equal, temp);
3003                      child.path = CopyString(temp);
3004                   }
3005                   child.nodeType = file;
3006                   child.parent = parent;
3007                   child.indent = parent.indent + 1;
3008                   child.type = file;
3009                   child.icon = NodeIcons::SelectFileIcon(child.name);
3010                   parent.files.Add(child);
3011                   node = child;
3012                   //child = null;
3013                }
3014                else
3015                {
3016                   StripLastDirectory(parentPath, parentPath);
3017                   parent = parent.parent;
3018                }
3019             }
3020          }
3021          else if(buffer[0] == '+')
3022          {
3023             equal = &buffer[0];
3024             equal[0] = ' ';
3025             TrimLSpaces(equal, equal);
3026             if(!strcmpi(section, "Target") && (!strcmpi(subSection, "Files") || !strcmpi(subSection, "Resources")))
3027             {
3028                char temp[MAX_LOCATION];
3029                ProjectNode child { };
3030                // NEW: Folders now have a path set like files
3031                child.name = CopyString(equal);
3032                strcpy(temp, parentPath);
3033                PathCatSlash(temp, child.name);
3034                child.path = CopyString(temp);
3035
3036                child.parent = parent;
3037                child.indent = parent.indent + 1;
3038                child.type = folder;
3039                child.nodeType = folder;
3040                child.files = { };
3041                child.icon = folder;
3042                PathCatSlash(parentPath, child.name);
3043                parent.files.Add(child);
3044                parent = child;
3045                node = child;
3046                //child = null;
3047             }
3048             else if(!strcmpi(section, "Configurations"))
3049             {
3050                ProjectConfig newConfig
3051                {
3052                   makingModified = true;
3053                   options = { };
3054                };
3055                f.Seek(pos, start);
3056                LegacyProjectConfigLoad(newConfig, f);
3057                project.configurations.Add(newConfig);
3058             }
3059          }
3060          else if(!strcmpi(buffer, "ECERE Project File"));
3061          else if(!strcmpi(buffer, "Version 0a"))
3062             ; //strcpy(version, "0a");
3063          else if(!strcmpi(buffer, "Version 0.1a"))
3064             ; //strcpy(version, "0.1a");
3065          else if(!strcmpi(buffer, "Configurations"))
3066          {
3067             project.configurations.Free();
3068             project.config = null;
3069             strcpy(section, buffer);
3070             configurationsPresent = true;
3071          }
3072          else if(!strcmpi(buffer, "Target") || !strnicmp(buffer, "Target \"", strlen("Target \"")))
3073          {
3074             ProjectConfig newConfig { name = CopyString("Default"), options = { } };
3075             char topNodePath[MAX_LOCATION];
3076             // newConfig.defaultNameSpace = "";
3077             //newConfig.objDir.dir = "obj";
3078             //newConfig.targetDir.dir = "";
3079             project = Project { /*options = { }*/ };
3080             project.configurations = { [ newConfig ] };
3081             project.config = newConfig;
3082             // if(project.topNode.path) delete project.topNode.path;
3083             GetWorkingDir(topNodePath, sizeof(topNodePath)-1);
3084             MakeSlashPath(topNodePath);
3085             PathCatSlash(topNodePath, filePath);
3086             project.filePath = topNodePath;
3087             parentPath[0] = '\0';
3088             parent = project.topNode;
3089             node = parent;
3090             strcpy(section, "Target");
3091             equal = &buffer[6];
3092             if(equal[0] == ' ')
3093             {
3094                equal++;
3095                if(equal[0] == '\"')
3096                {
3097                   StripQuotes(equal, equal);
3098                   delete project.moduleName; project.moduleName = CopyString(equal);
3099                }
3100             }
3101          }
3102          else if(!strcmpi(buffer, "Compiler Options"));
3103          else if(!strcmpi(buffer, "IncludeDirs"))
3104             strcpy(subSection, buffer);
3105          else if(!strcmpi(buffer, "Linker Options"));
3106          else if(!strcmpi(buffer, "LibraryDirs"))
3107             strcpy(subSection, buffer);
3108          else if(!strcmpi(buffer, "Files"))
3109          {
3110             strcpy(section, "Target");
3111             strcpy(subSection, buffer);
3112          }
3113          else if(!strcmpi(buffer, "Resources"))
3114          {
3115             ProjectNode child { };
3116             parent.files.Add(child);
3117             child.parent = parent;
3118             child.indent = parent.indent + 1;
3119             child.name = CopyString(buffer);
3120             child.path = CopyString("");
3121             child.type = resources;
3122             child.files = { };
3123             child.icon = archiveFile;
3124             project.resNode = child;
3125             parent = child;
3126             node = child;
3127             strcpy(subSection, buffer);
3128          }
3129          else
3130          {
3131             equal = strstr(buffer, "=");
3132             if(equal)
3133             {
3134                equal[0] = '\0';
3135                TrimRSpaces(buffer, buffer);
3136                equal++;
3137                TrimLSpaces(equal, equal);
3138
3139                if(!strcmpi(section, "Target"))
3140                {
3141                   if(!strcmpi(buffer, "Build Exclusions"))
3142                   {
3143                      if(!strcmpi(section, "Target") && !strcmpi(subSection, "Files"))
3144                      {
3145                         /*if(node && node.type != NodeTypes::project)
3146                            ParseListValue(node.buildExclusions, equal);*/
3147                      }
3148                   }
3149                   else if(!strcmpi(buffer, "Path") && !strcmpi(subSection, "Resources"))
3150                   {
3151                      delete project.resNode.path;
3152                      project.resNode.path = CopyString(equal);
3153                      PathCatSlash(parentPath, equal);
3154                   }
3155
3156                   // Config Settings
3157                   else if(!strcmpi(buffer, "Intermediate Directory"))
3158                      project.config.options.objectsDirectory = /*CopyString(*/equal/*)*/; //objDir.expression = equal;
3159                   else if(!strcmpi(buffer, "Debug"))
3160                      project.config.options.debug = ParseTrueFalseValue(equal);
3161                   else if(!strcmpi(buffer, "Optimize"))
3162                   {
3163                      if(!strcmpi(equal, "None"))
3164                         project.config.options.optimization = none;
3165                      else if(!strcmpi(equal, "Speed") || !strcmpi(equal, "True"))
3166                         project.config.options.optimization = speed;
3167                      else if(!strcmpi(equal, "Size"))
3168                         project.config.options.optimization = size;
3169                      else
3170                         project.config.options.optimization = none;
3171                   }
3172                   else if(!strcmpi(buffer, "Profile"))
3173                      project.config.options.profile = ParseTrueFalseValue(equal);
3174                   else if(!strcmpi(buffer, "MemoryGuard"))
3175                      project.config.options.memoryGuard = ParseTrueFalseValue(equal);
3176                   else
3177                   {
3178                      if(!project.options) project.options = { };
3179
3180                      // Project Wide Settings (All configs)
3181                      if(!strcmpi(buffer, "Target Name"))
3182                         project.options.targetFileName = /*CopyString(*/equal/*)*/;
3183                      else if(!strcmpi(buffer, "Target Type"))
3184                      {
3185                         if(!strcmpi(equal, "Executable"))
3186                            project.options.targetType = executable;
3187                         else if(!strcmpi(equal, "Shared"))
3188                            project.options.targetType = sharedLibrary;
3189                         else if(!strcmpi(equal, "Static"))
3190                            project.options.targetType = staticLibrary;
3191                         else
3192                            project.options.targetType = executable;
3193                      }
3194                      else if(!strcmpi(buffer, "Target Directory"))
3195                         project.options.targetDirectory = /*CopyString(*/equal/*)*/;
3196                      else if(!strcmpi(buffer, "Console"))
3197                         project.options.console = ParseTrueFalseValue(equal);
3198                      else if(!strcmpi(buffer, "Libraries"))
3199                      {
3200                         if(!project.options.libraries) project.options.libraries = { };
3201                         ParseArrayValue(project.options.libraries, equal);
3202                      }
3203                      else if(!strcmpi(buffer, "AllWarnings"))
3204                         project.options.warnings = ParseTrueFalseValue(equal) ? all : unset;
3205                      else if(!strcmpi(buffer, "Preprocessor Definitions"))
3206                      {
3207                         if(!strcmpi(section, "Target") && !strcmpi(subSection, "Files"))
3208                         {
3209                            /*if(node && (node.type == NodeTypes::project || (node.type == file && !node.isInResources) || node.type == folder))
3210                               ParseListValue(node.preprocessorDefs, equal);*/
3211                         }
3212                         else
3213                         {
3214                            if(!project.options.preprocessorDefinitions) project.options.preprocessorDefinitions = { };
3215                            ParseArrayValue(project.options.preprocessorDefinitions, equal);
3216                         }
3217                      }
3218                   }
3219                }
3220             }
3221          }
3222       }
3223    }
3224    parent = null;
3225
3226    SplitPlatformLibraries(project);
3227
3228    if(configurationsPresent)
3229       CombineIdenticalConfigOptions(project);
3230    return project;
3231 }
3232
3233 void SplitPlatformLibraries(Project project)
3234 {
3235    if(project && project.configurations)
3236    {
3237       for(cfg : project.configurations)
3238       {
3239          if(cfg.options.libraries && cfg.options.libraries.count)
3240          {
3241             Iterator<String> it { cfg.options.libraries };
3242             while(it.Next())
3243             {
3244                String l = it.data;
3245                char * platformName = strstr(l, ":");
3246                if(platformName)
3247                {
3248                   PlatformOptions platform = null;
3249                   platformName++;
3250                   if(!cfg.platforms) cfg.platforms = { };
3251                   for(p : cfg.platforms)
3252                   {
3253                      if(!strcmpi(platformName, p.name))
3254                      {
3255                         platform = p;
3256                         break;
3257                      }
3258                   }
3259                   if(!platform)
3260                   {
3261                      platform = { name = CopyString(platformName), options = { libraries = { } } };
3262                      cfg.platforms.Add(platform);
3263                   }
3264                   *(platformName-1) = 0;
3265                   platform.options.libraries.Add(CopyString(l));
3266
3267                   cfg.options.libraries.Delete(it.pointer);
3268                   it.pointer = null;
3269                }
3270             }
3271          }
3272       }      
3273    }
3274 }
3275
3276 void CombineIdenticalConfigOptions(Project project)
3277 {
3278    if(project && project.configurations && project.configurations.count)
3279    {
3280       DataMember member;
3281       ProjectOptions nullOptions { };
3282       ProjectConfig firstConfig = null;
3283       for(cfg : project.configurations)
3284       {
3285          if(cfg.options.targetType != staticLibrary)
3286          {
3287             firstConfig = cfg;
3288             break;
3289          }
3290       }
3291       if(!firstConfig)
3292          firstConfig = project.configurations.firstIterator.data;
3293
3294       for(member = class(ProjectOptions).membersAndProperties.first; member; member = member.next)
3295       {
3296          if(!member.isProperty)
3297          {
3298             Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
3299             if(type)
3300             {
3301                bool same = true;
3302
3303                for(cfg : project.configurations)
3304                {
3305                   if(cfg != firstConfig)
3306                   {
3307                      if(cfg.options.targetType != staticLibrary)
3308                      {
3309                         int result;
3310                         
3311                         if(type.type == noHeadClass || type.type == normalClass)
3312                         {
3313                            result = type._vTbl[__ecereVMethodID_class_OnCompare](type, 
3314                               *(void **)((byte *)firstConfig.options + member.offset + member._class.offset),
3315                               *(void **)((byte *)cfg.options         + member.offset + member._class.offset));
3316                         }
3317                         else
3318                         {
3319                            result = type._vTbl[__ecereVMethodID_class_OnCompare](type, 
3320                               (byte *)firstConfig.options + member.offset + member._class.offset,
3321                               (byte *)cfg.options         + member.offset + member._class.offset);
3322                         }
3323                         if(result)
3324                         {
3325                            same = false;
3326                            break;
3327                         }
3328                      }
3329                   }                  
3330                }
3331                if(same)
3332                {
3333                   if(type.type == noHeadClass || type.type == normalClass)
3334                   {
3335                      if(!type._vTbl[__ecereVMethodID_class_OnCompare](type, 
3336                         *(void **)((byte *)firstConfig.options + member.offset + member._class.offset),
3337                         *(void **)((byte *)nullOptions         + member.offset + member._class.offset)))
3338                         continue;
3339                   }
3340                   else
3341                   {
3342                      if(!type._vTbl[__ecereVMethodID_class_OnCompare](type, 
3343                         (byte *)firstConfig.options + member.offset + member._class.offset,
3344                         (byte *)nullOptions         + member.offset + member._class.offset))
3345                         continue;
3346                   }
3347
3348                   if(!project.options) project.options = { };
3349                   
3350                   /*if(type.type == noHeadClass || type.type == normalClass)
3351                   {
3352                      type._vTbl[__ecereVMethodID_class_OnCopy](type, 
3353                         (byte *)project.options + member.offset + member._class.offset,
3354                         *(void **)((byte *)firstConfig.options + member.offset + member._class.offset));
3355                   }
3356                   else
3357                   {
3358                      void * address = (byte *)firstConfig.options + member.offset + member._class.offset;
3359                      // TOFIX: ListBox::SetData / OnCopy mess
3360                      type._vTbl[__ecereVMethodID_class_OnCopy](type, 
3361                         (byte *)project.options + member.offset + member._class.offset,
3362                         (type.typeSize > 4) ? address : 
3363                            ((type.typeSize == 4) ? (void *)*(uint32 *)address : 
3364                               ((type.typeSize == 2) ? (void *)*(uint16*)address : 
3365                                  (void *)*(byte *)address )));                              
3366                   }*/
3367                   memcpy(
3368                      (byte *)project.options + member.offset + member._class.offset,
3369                      (byte *)firstConfig.options + member.offset + member._class.offset, type.typeSize);
3370
3371                   for(cfg : project.configurations)
3372                   {
3373                      if(cfg.options.targetType == staticLibrary)
3374                      {
3375                         int result;
3376                         
3377                         if(type.type == noHeadClass || type.type == normalClass)
3378                         {
3379                            result = type._vTbl[__ecereVMethodID_class_OnCompare](type, 
3380                               *(void **)((byte *)firstConfig.options + member.offset + member._class.offset),
3381                               *(void **)((byte *)cfg.options         + member.offset + member._class.offset));
3382                         }
3383                         else
3384                         {
3385                            result = type._vTbl[__ecereVMethodID_class_OnCompare](type, 
3386                               (byte *)firstConfig.options + member.offset + member._class.offset,
3387                               (byte *)cfg.options         + member.offset + member._class.offset);
3388                         }
3389                         if(result)
3390                            continue;
3391                      }
3392                      if(cfg != firstConfig)
3393                      {
3394                         if(type.type == noHeadClass || type.type == normalClass)
3395                         {
3396                            type._vTbl[__ecereVMethodID_class_OnFree](type, 
3397                               *(void **)((byte *)cfg.options + member.offset + member._class.offset));
3398                         }
3399                         else
3400                         {
3401                            type._vTbl[__ecereVMethodID_class_OnFree](type, 
3402                               (byte *)cfg.options + member.offset + member._class.offset);
3403                         }
3404                         memset((byte *)cfg.options + member.offset + member._class.offset, 0, type.typeSize);
3405                      }                     
3406                   }
3407                   memset((byte *)firstConfig.options + member.offset + member._class.offset, 0, type.typeSize);
3408                }
3409             }
3410          }
3411       }
3412       delete nullOptions;
3413
3414       // Compare Platform Specific Settings
3415       {
3416          bool same = true;
3417          for(cfg : project.configurations)
3418          {
3419             if(cfg != firstConfig && cfg.options.targetType != staticLibrary && (firstConfig.platforms || cfg.platforms) &&
3420                ((!firstConfig.platforms && cfg.platforms) || firstConfig.platforms.OnCompare(cfg.platforms)))
3421             {
3422                same = false;
3423                break;
3424             }
3425          }
3426          if(same && firstConfig.platforms)
3427          {
3428             for(cfg : project.configurations)
3429             {
3430                if(cfg.options.targetType == staticLibrary && firstConfig.platforms.OnCompare(cfg.platforms))
3431                   continue;
3432                if(cfg != firstConfig)
3433                {
3434                   cfg.platforms.Free();
3435                   delete cfg.platforms;
3436                }
3437             }
3438             project.platforms = firstConfig.platforms;
3439             firstConfig.platforms = null;
3440          }
3441       }
3442
3443       // Static libraries can't contain libraries
3444       for(cfg : project.configurations)
3445       {
3446          if(cfg.options.targetType == staticLibrary)
3447          {
3448             if(!cfg.options.libraries) cfg.options.libraries = { };
3449             cfg.options.libraries.Free();
3450          }
3451       }
3452    }
3453 }
3454
3455 Project LoadProject(char * filePath)
3456 {
3457    Project project = null;
3458    File f = FileOpen(filePath, read);
3459    if(f)
3460    {
3461       project = LegacyBinaryLoadProject(f, filePath);
3462       if(!project)
3463       {
3464          JSONParser parser { f = f };
3465          JSONResult result = parser.GetObject(class(Project), &project);
3466          if(project)
3467          {
3468             char insidePath[MAX_LOCATION];
3469
3470             delete project.topNode.files;
3471             if(!project.files) project.files = { };
3472             project.topNode.files = project.files;
3473             project.resNode = project.topNode.Add(project, "Resources", project.topNode.files.last, resources, archiveFile, false);
3474             delete project.resNode.path;
3475             project.resNode.path = project.resourcesPath;
3476             project.resourcesPath = null;
3477             project.resNode.nodeType = (ProjectNodeType)-1;
3478             delete project.resNode.files;
3479             project.resNode.files = project.resources;
3480             project.files = null;
3481             project.resources = null;
3482             if(!project.configurations) project.configurations = { };
3483
3484             {
3485                char topNodePath[MAX_LOCATION];
3486                GetWorkingDir(topNodePath, sizeof(topNodePath)-1);
3487                MakeSlashPath(topNodePath);
3488                PathCatSlash(topNodePath, filePath);
3489                project.filePath = topNodePath;//filePath;
3490             }
3491
3492             project.topNode.FixupNode(insidePath);
3493          }
3494          delete parser;
3495       }
3496       if(!project)
3497          project = LegacyAsciiLoadProject(f, filePath);
3498
3499       delete f;
3500
3501       if(project)
3502       {
3503          if(!project.options) project.options = { };
3504          if(!project.config && project.configurations)
3505             project.config = project.configurations.firstIterator.data;
3506
3507          if(!project.resNode)
3508          {
3509             project.resNode = project.topNode.Add(project, "Resources", project.topNode.files.last, resources, archiveFile, false);
3510          }
3511          
3512          if(!project.moduleName)
3513             project.moduleName = CopyString(project.name);
3514          if(project.config && 
3515             (!project.options || !project.options.targetFileName || !project.options.targetFileName[0]) &&
3516             (!project.config.options.targetFileName || !project.config.options.targetFileName[0]))
3517          {
3518             //delete project.config.options.targetFileName;
3519             
3520             project.options.targetFileName = /*CopyString(*/project.moduleName/*)*/;
3521             project.config.options.optimization = none;
3522             project.config.options.debug = true;
3523             //project.config.options.warnings = unset;
3524             project.config.options.memoryGuard = false;
3525             project.config.compilingModified = true;
3526             project.config.linkingModified = true;
3527          }
3528          else if(!project.topNode.name && project.config)
3529          {
3530             project.topNode.name = CopyString(project.config.options.targetFileName);
3531          }
3532
3533          /* // THIS IS NOW AUTOMATED WITH A project CHECK IN ProjectNode
3534          project.topNode.configurations = project.configurations;
3535          project.topNode.platforms = project.platforms;
3536          project.topNode.options = project.options;*/
3537       }
3538    }
3539    return project;
3540 }