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