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