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