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