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