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