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