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