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