ide; windres support for forced 32 bit builds. and showing resources.rc instead of...
[sdk] / ide / src / IDESettings.ec
1 #ifdef ECERE_STATIC
2 import static "ecere"
3 #else
4 import "ecere"
5 #endif
6
7 import "StringsBox"
8
9 import "OldIDESettings"
10
11 define MaxRecent = 9;
12
13 enum DirTypes { includes, libraries, executables };
14
15 define defaultCompilerName = "Default";
16
17 define defaultObjDirExpression = "obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)";
18
19 char * settingsDirectoryNames[DirTypes] = 
20 {
21    "Include Files",
22    "Library Files",
23    "Executable Files"
24 };
25
26 enum GlobalSettingsChange { none, editorSettings, projectOptions, compilerSettings };
27
28 enum PathRelationship { unrelated, identical, siblings, subPath, parentPath, insuficientInput, pathEmpty, toEmpty, pathNull, toNull, bothEmpty, bothNull };
29 PathRelationship eString_PathRelated(char * path, char * to, char * pathDiff)
30 {
31    PathRelationship result;
32    if(pathDiff) *pathDiff = '\0';
33    if(path && *path && to && *to)
34    {
35       char toPart[MAX_FILENAME], toRest[MAX_LOCATION];
36       char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION];
37       strcpy(toRest, to);
38       strcpy(pathRest, path);
39       for(; toRest[0] && pathRest[0];)
40       {
41          SplitDirectory(toRest, toPart, toRest);
42          SplitDirectory(pathRest, pathPart, pathRest);
43          if(!fstrcmp(pathPart, toPart)) result = siblings;
44          else break;
45       }
46       if(result == siblings)
47       {
48          if(!*toRest && !*pathRest) result = identical;
49          else if(!*pathRest) result = parentPath;
50          else result = subPath;
51          if(pathDiff && result != identical) strcpy(pathDiff, *pathRest == '\0' ? toRest : pathRest);
52       }
53       else result = unrelated;
54    }
55    else
56    {
57       if(path && to)
58       {
59          if(!*path && !*to) result = bothEmpty;
60          else if(!*path) result = pathEmpty;
61          else result = toEmpty;
62       }
63       else if(!path && !to) result = bothNull;
64       else if(!path) result = pathNull;
65       else result = toNull;
66    }
67    return result;
68 }
69
70 char * CopyValidateMakefilePath(char * path)
71 {
72    const int map[]  =    {           0,           1,             2,             3,           4,                    5,                 6,            0,                   1,                    2,        7 };
73    const char * vars[] = { "$(MODULE)", "$(CONFIG)", "$(PLATFORM)", "$(COMPILER)", "$(TARGET)", "$(COMPILER_SUFFIX)", "$(DEBUG_SUFFIX)", "$(PROJECT)",  "$(CONFIGURATION)", "$(TARGET_PLATFORM)",(char *)0 };
74
75    char * copy = null;
76    if(path)
77    {
78       int len;
79       len = strlen(path);
80       copy = CopyString(path);
81       if(len)
82       {
83          int c;
84          char * tmp = copy;
85          char * start = tmp;
86          Array<char *> parts { };
87          
88          for(c=0; c<len; c++)
89          {
90             if(tmp[c] == '$')
91             {
92                int v;
93                for(v=0; vars[v]; v++)
94                {
95                   if(SearchString(&tmp[c], 0, vars[v], false, false) == &tmp[c])
96                   {
97                      tmp[c] = '\0';
98                      parts.Add(start);
99                      parts.Add(vars[map[v]]);
100                      c += strlen(vars[v]);
101                      start = &tmp[c];
102                      c--;
103                      break;
104                   }
105                }
106             }
107          }
108          if(start[0])
109             parts.Add(start);
110
111          if(parts.count)
112          {
113             /*int c, */len = 0;
114             for(c=0; c<parts.count; c++) len += strlen(parts[c]);
115             copy = new char[++len];
116             copy[0] = '\0';
117             for(c=0; c<parts.count; c++) strcat(copy, parts[c]);
118          }
119          else
120             copy = null;
121          delete parts;
122          delete tmp;
123       }
124    }
125    return copy;
126 }
127
128 CompilerConfig MakeDefaultCompiler(char * name, bool readOnly)
129 {
130    CompilerConfig defaultCompiler
131    {
132       name,
133       readOnly,
134       gcc,
135       GetRuntimePlatform(),
136       1,
137       makeDefaultCommand,
138       ecpDefaultCommand,
139       eccDefaultCommand,
140       ecsDefaultCommand,
141       earDefaultCommand,
142       cppDefaultCommand,
143       ccDefaultCommand,
144       cxxDefaultCommand
145    };
146    incref defaultCompiler;
147    return defaultCompiler;
148 }
149
150 class IDESettingsContainer : GlobalSettings
151 {
152 #ifdef SETTINGS_TEST
153    settingsName = "ecereIDESettingsTest";
154 #else
155    settingsName = "ecereIDE";
156 #endif
157
158    virtual void OnLoad(GlobalSettingsData data);
159
160    char moduleLocation[MAX_LOCATION];
161
162 private:
163    IDESettingsContainer()
164    {
165       char path[MAX_LOCATION];
166       char * start;
167       LocateModule(null, moduleLocation);
168       strcpy(path, moduleLocation);
169       StripLastDirectory(moduleLocation, moduleLocation);
170       ChangeCh(moduleLocation, '\\', '/');
171       // PortableApps.com directory structure
172       if((start = strstr(path, "\\App\\EcereSDK\\bin\\ide.exe")))
173       {
174          char configFilePath[MAX_LOCATION];
175          char defaultConfigFilePath[MAX_LOCATION];
176
177          start[0] = '\0';
178
179          strcpy(configFilePath, path);
180          PathCat(configFilePath, "Data");
181          PathCat(configFilePath, "ecereIDE.ini");
182
183          strcpy(defaultConfigFilePath, path);
184          PathCat(defaultConfigFilePath, "App");
185          PathCat(defaultConfigFilePath, "DefaultData");
186          PathCat(defaultConfigFilePath, "ecereIDE.ini");
187          
188          if(FileExists(defaultConfigFilePath))
189          {
190             if(!FileExists(configFilePath))
191             {
192                File f = FileOpen(defaultConfigFilePath, read);
193                f.CopyTo(configFilePath);
194                f.Flush();
195                delete f;
196             }
197             PathCat(path, "Data");
198             // the forced settings location will only be
199             // used if the running ide's path matches 
200             // the PortableApps.com directory structure
201             // and the default ini file is found in 
202             // the DefaultData directory
203             settingsLocation = path;
204             portable = true;
205          }
206       }
207    }
208
209    void OnAskReloadSettings()
210    {
211       /*if(MessageBox { type = YesNo, master = this, 
212             text = "Global Settings Modified Externally", 
213             contents = "The global settings were modified by another instance.\n"
214             "Would you like to reload them?" }.Modal() == Yes)*/
215       {
216          Load();
217       }
218    }
219
220    SettingsIOResult Load()
221    {
222       SettingsIOResult result = GlobalSettings::Load();
223       IDESettings data = (IDESettings)this.data;
224       CompilerConfig defaultCompiler = null;
225       if(!data)
226       {
227          this.data = IDESettings { };
228          if(dataOwner)
229             *dataOwner = this.data;
230
231          if(result == fileNotCompatibleWithDriver)
232          {
233             bool loaded;
234             OldIDESettings oldSettings { };
235             Close();
236             loaded = oldSettings.Load() == success;
237             oldSettings.Close();
238             if(loaded)
239             {
240                data = (IDESettings)this.data;
241
242                for(c : oldSettings.compilerConfigs)
243                   data.compilerConfigs.Add(c.Copy());
244
245                for(s : oldSettings.recentFiles) data.recentFiles.Add(CopyString(s));
246                for(s : oldSettings.recentProjects) data.recentProjects.Add(CopyString(s));
247
248                data.docDir = oldSettings.docDir;
249                data.ideFileDialogLocation = oldSettings.ideFileDialogLocation;
250                data.ideProjectFileDialogLocation = oldSettings.ideProjectFileDialogLocation;
251                data.useFreeCaret = oldSettings.useFreeCaret;
252                data.showLineNumbers = oldSettings.showLineNumbers;
253                data.caretFollowsScrolling = oldSettings.caretFollowsScrolling;
254                delete data.displayDriver; data.displayDriver = CopyString(oldSettings.displayDriver);
255                data.projectDefaultTargetDir = oldSettings.projectDefaultTargetDir;
256                data.projectDefaultIntermediateObjDir = oldSettings.projectDefaultIntermediateObjDir;
257                         
258                Save();
259                result = success;
260             }
261             delete oldSettings;
262          }
263          if(result == fileNotFound || !data)
264          {
265             data = (IDESettings)this.data;
266             data.useFreeCaret = true;
267             data.showLineNumbers = true;
268             data.caretFollowsScrolling = true;
269          }
270       }
271       // Ensure we have a default compiler
272       defaultCompiler = data.GetCompilerConfig(defaultCompilerName);
273       if(!defaultCompiler)
274       {
275          defaultCompiler = MakeDefaultCompiler(defaultCompilerName, true);
276          data.compilerConfigs.Add(defaultCompiler);
277       }
278
279       // We incref the compilers below, so reset refCount to 0
280       defaultCompiler._refCount = 0;
281
282       CloseAndMonitor();
283       if(data.compilerConfigs)
284       {
285          for(c : data.compilerConfigs)
286          {
287             CompilerConfig compiler = c;
288             char * cxxCommand = compiler.cxxCommand;
289             if(!cxxCommand || !cxxCommand[0])
290                compiler.cxxCommand = cxxDefaultCommand;
291             incref compiler;
292          }
293       }
294       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
295          data.ManagePortablePaths(moduleLocation, true);
296       data.ForcePathSeparatorStyle(true);
297       OnLoad(data);
298       return result;
299    }
300
301    SettingsIOResult Save()
302    {
303       SettingsIOResult result;
304
305       IDESettings data = (IDESettings)this.data;
306       Platform runtimePlatform = GetRuntimePlatform();
307       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
308          data.ManagePortablePaths(moduleLocation, false);
309       data.ForcePathSeparatorStyle(true);
310       result = GlobalSettings::Save();
311       if(result != success)
312          PrintLn("Error saving IDE settings");
313       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
314          data.ManagePortablePaths(moduleLocation, true);
315       CloseAndMonitor();
316       return result;
317    }
318 }
319
320 class IDESettings : GlobalSettingsData
321 {
322 public:
323    List<CompilerConfig> compilerConfigs { };
324    Array<String> recentFiles { };
325    Array<String> recentProjects { };
326    property char * docDir
327    {
328       set { delete docDir; if(value && value[0]) docDir = CopyString(value); }
329       get { return docDir ? docDir : ""; }
330       isset { return docDir && docDir[0]; }
331    }
332    property char * ideFileDialogLocation
333    {
334       set { delete ideFileDialogLocation; if(value && value[0]) ideFileDialogLocation = CopyString(value); }
335       get { return ideFileDialogLocation ? ideFileDialogLocation : ""; }
336       isset { return ideFileDialogLocation && ideFileDialogLocation[0]; }
337    }
338    property char * ideProjectFileDialogLocation
339    {
340       set { delete ideProjectFileDialogLocation; if(value && value[0]) ideProjectFileDialogLocation = CopyString(value); }
341       get { return ideProjectFileDialogLocation ? ideProjectFileDialogLocation : ""; }
342       isset { return ideProjectFileDialogLocation && ideProjectFileDialogLocation[0]; }
343    }
344    bool useFreeCaret;
345    bool showLineNumbers;
346    bool caretFollowsScrolling;
347    char * displayDriver;
348    
349    // TODO: Classify settings
350    //EditorSettings editor { };
351
352    property char * projectDefaultTargetDir
353    {
354       set { delete projectDefaultTargetDir; if(value && value[0]) projectDefaultTargetDir = CopyValidateMakefilePath(value); }
355       get { return projectDefaultTargetDir ? projectDefaultTargetDir : ""; }
356       isset { return projectDefaultTargetDir && projectDefaultTargetDir[0]; }
357    }
358    property char * projectDefaultIntermediateObjDir
359    {
360       set { delete projectDefaultIntermediateObjDir; if(value && value[0]) projectDefaultIntermediateObjDir = CopyValidateMakefilePath(value); }
361       get { return projectDefaultIntermediateObjDir ? projectDefaultIntermediateObjDir : ""; }
362       isset { return projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0]; }
363    }
364
365    property char * compilerConfigsDir
366    {
367       set { delete compilerConfigsDir; if(value && value[0]) compilerConfigsDir = CopyString(value); }
368       get { return compilerConfigsDir ? compilerConfigsDir : ""; }
369       isset { return compilerConfigsDir && compilerConfigsDir[0]; }
370    }
371
372    property char * defaultCompiler
373    {
374       set { delete defaultCompiler; if(value && value[0]) defaultCompiler = CopyString(value); }
375       get { return defaultCompiler && defaultCompiler[0] ? defaultCompiler : defaultCompilerName; }
376       isset { return defaultCompiler && defaultCompiler[0]; }
377    }
378
379 private:
380    char * docDir;
381    char * ideFileDialogLocation;
382    char * ideProjectFileDialogLocation;
383    char * projectDefaultTargetDir;
384    char * projectDefaultIntermediateObjDir;
385    char * compilerConfigsDir;
386    char * defaultCompiler;
387
388    CompilerConfig GetCompilerConfig(String compilerName)
389    {
390       char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
391       CompilerConfig compilerConfig = null;
392       for(compiler : compilerConfigs)
393       {
394          if(!strcmp(compiler.name, name))
395          {
396             compilerConfig = compiler;
397             break;
398          }
399       }
400       if(!compilerConfig && compilerConfigs.count)
401          compilerConfig = compilerConfigs.firstIterator.data;
402       if(compilerConfig)
403          incref compilerConfig;
404       return compilerConfig;
405    }
406
407    ~IDESettings()
408    {
409       compilerConfigs.Free();
410       delete compilerConfigs;
411       recentFiles.Free();
412       delete recentFiles;
413       recentProjects.Free();
414       delete recentProjects;
415       delete docDir;
416    
417       delete projectDefaultTargetDir;
418       delete projectDefaultIntermediateObjDir;
419       delete compilerConfigsDir;
420       delete defaultCompiler;
421
422       delete ideFileDialogLocation;
423       delete ideProjectFileDialogLocation;
424       delete displayDriver;
425    }
426
427    void ForcePathSeparatorStyle(bool unixStyle)
428    {
429       char from, to;
430       if(unixStyle)
431          from = '\\', to = '/';
432       else
433          from = '/', to = '\\';
434       if(compilerConfigs && compilerConfigs.count)
435       {
436          int i;
437          for(config : compilerConfigs)
438          {
439             if(config.includeDirs && config.includeDirs.count)
440             {
441                for(i = 0; i < config.includeDirs.count; i++)
442                {
443                   if(config.includeDirs[i] && config.includeDirs[i][0])
444                      ChangeCh(config.includeDirs[i], from, to);
445                }
446             }
447             if(config.libraryDirs && config.libraryDirs.count)
448             {
449                for(i = 0; i < config.libraryDirs.count; i++)
450                {
451                   if(config.libraryDirs[i] && config.libraryDirs[i][0])
452                      ChangeCh(config.libraryDirs[i], from, to);
453                }
454             }
455             if(config.executableDirs && config.executableDirs.count)
456             {
457                for(i = 0; i < config.executableDirs.count; i++)
458                {
459                   if(config.executableDirs[i] && config.executableDirs[i][0])
460                      ChangeCh(config.executableDirs[i], from, to);
461                }
462             }
463          }
464       }
465       if(recentFiles && recentFiles.count)
466       {
467          int c;
468          for(c = 0; c < recentFiles.count; c++)
469          {
470             if(recentFiles[c] && recentFiles[c][0])
471                ChangeCh(recentFiles[c], from, to);
472          }
473       }
474       if(recentProjects && recentProjects.count)
475       {
476          int c;
477          for(c = 0; c < recentProjects.count; c++)
478          {
479             if(recentProjects[c] && recentProjects[c][0])
480                ChangeCh(recentProjects[c], from, to);
481          }
482       }
483       if(docDir && docDir[0])
484          ChangeCh(docDir, from, to);
485       if(ideFileDialogLocation && ideFileDialogLocation[0])
486          ChangeCh(ideFileDialogLocation, from, to);
487       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
488          ChangeCh(ideProjectFileDialogLocation, from, to);
489
490       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
491          ChangeCh(projectDefaultTargetDir, from, to);
492       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
493          ChangeCh(projectDefaultIntermediateObjDir, from, to);
494
495       if(compilerConfigsDir && compilerConfigsDir[0])
496          ChangeCh(compilerConfigsDir, from, to);
497    }
498
499    void ManagePortablePaths(char * location, bool makeAbsolute)
500    {
501       int c;
502       if(compilerConfigs && compilerConfigs.count)
503       {
504          for(config : compilerConfigs)
505          {
506             DirTypes t;
507             for(t = 0; t < DirTypes::enumSize; t++)
508             {
509                Array<String> dirs = null;
510                if(t == executables) dirs = config.executableDirs;
511                else if(t == includes) dirs = config.includeDirs;
512                else if(t == libraries) dirs = config.libraryDirs;
513                if(dirs && dirs.count)
514                {
515                   for(c = 0; c < dirs.count; c++)
516                   {
517                      if(dirs[c] && dirs[c][0])
518                         dirs[c] = UpdatePortablePath(dirs[c], location, makeAbsolute);
519                   }
520                }
521             }
522          }
523       }
524       if(recentFiles && recentFiles.count)
525       {
526          for(c = 0; c < recentFiles.count; c++)
527          {
528             if(recentFiles[c] && recentFiles[c][0])
529                recentFiles[c] = UpdatePortablePath(recentFiles[c], location, makeAbsolute);
530          }
531       }
532       if(recentProjects && recentProjects.count)
533       {
534          for(c = 0; c < recentProjects.count; c++)
535          {
536             if(recentProjects[c] && recentProjects[c][0])
537                recentProjects[c] = UpdatePortablePath(recentProjects[c], location, makeAbsolute);
538          }
539       }
540       if(docDir && docDir[0])
541          docDir = UpdatePortablePath(docDir, location, makeAbsolute);
542       if(ideFileDialogLocation && ideFileDialogLocation[0])
543          ideFileDialogLocation = UpdatePortablePath(ideFileDialogLocation, location, makeAbsolute);
544       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
545          ideProjectFileDialogLocation = UpdatePortablePath(ideProjectFileDialogLocation, location, makeAbsolute);
546
547       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
548          projectDefaultTargetDir = UpdatePortablePath(projectDefaultTargetDir, location, makeAbsolute);
549       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
550          projectDefaultIntermediateObjDir = UpdatePortablePath(projectDefaultIntermediateObjDir, location, makeAbsolute);
551
552       if(compilerConfigsDir && compilerConfigsDir[0])
553          compilerConfigsDir = UpdatePortablePath(compilerConfigsDir, location, makeAbsolute);
554    }
555
556    char * UpdatePortablePath(char * path, char * location, bool makeAbsolute)
557    {
558       char * output;
559       if(makeAbsolute)
560       {
561          char p[MAX_LOCATION];
562          strcpy(p, location);
563          PathCatSlash(p, path);
564          delete path;
565          output = CopyString(p);
566       }
567       else
568       {
569          PathRelationship rel = eString_PathRelated(path, location, null);
570          if(rel == subPath || rel == identical)
571          {
572             char p[MAX_LOCATION];
573             MakePathRelative(path, location, p);
574             if(!*p) strcpy(p, "./");
575             else ChangeCh(p, '\\', '/');
576             delete path;
577             output = CopyString(p);
578          }
579          else
580             output = path;
581       }
582       return output;
583    }
584
585    void AddRecentFile(char * fileName)
586    {
587       int c;
588       char * filePath = CopyString(fileName);
589       ChangeCh(filePath, '\\', '/');
590       for(c = 0; c<recentFiles.count; c++)
591       {
592          if(recentFiles[c] && !fstrcmp(recentFiles[c], filePath))
593          {
594             recentFiles.Delete((void *)&recentFiles[c]);
595             c--;
596          }
597       }
598       while(recentFiles.count >= MaxRecent)
599          recentFiles.Delete(recentFiles.GetLast());
600       recentFiles.Insert(null, filePath);
601       //UpdateRecentMenus(owner);
602    }
603
604    void AddRecentProject(char * projectName)
605    {
606       int c;
607       char * filePath = CopyString(projectName);
608       ChangeCh(filePath, '\\', '/');
609       for(c = 0; c<recentProjects.count; c++)
610       {
611          if(recentProjects[c] && !fstrcmp(recentProjects[c], filePath))
612          {
613             recentProjects.Delete((void *)&recentProjects[c]);
614             c--;
615          }
616       }
617       while(recentProjects.count >= MaxRecent)
618          recentProjects.Delete(recentProjects.GetLast());
619       recentProjects.Insert(null, filePath);
620       //UpdateRecentMenus(owner);
621    }
622 }
623
624 static const char * compilerTypeNames[CompilerType] = { "GCC", "TCC", "PCC", "VS8", "VS9", "VS10" };
625 static const char * compilerTypeVersionString[CompilerType] = { "", "", "", "8.00", "9.00", "10.00" };
626 static const char * compilerTypeSolutionFileVersionString[CompilerType] = { "", "", "", "9.00", "10.00", "11.00" };
627 static const char * compilerTypeYearString[CompilerType] = { "", "", "", "2005", "2008", "2010" };
628 static const char * compilerTypeProjectFileExtension[CompilerType] = { "", "", "", "vcproj", "vcproj", "vcxproj" };
629 // TODO: i18n with Array
630 static Array<String> compilerTypeLongNames
631 { [
632    $"GNU Compiler Collection (GCC) / GNU Make",
633    $"Tiny C Compiler / GNU Make",
634    $"Portable C Compiler / GNU Make",
635    $"Microsoft Visual Studio 2005 (8.0) Compiler",
636    $"Microsoft Visual Studio 2008 (9.0) Compiler",
637    $"Microsoft Visual Studio 2010 (10.0) Compiler"
638 ] };
639 const CompilerType firstCompilerType = gcc;
640 const CompilerType lastCompilerType = vs10;
641 public enum CompilerType
642 {
643    gcc, tcc, pcc, vs8, vs9, vs10;
644
645    property bool isVC
646    {
647       get { return this == vs8 || this == vs9 || this == vs10; }
648    }
649
650    property char *
651    {
652       get { return OnGetString(null, null, null); }
653       set
654       {  
655          if(value)
656          {
657             Platform c;
658             for(c = firstCompilerType; c <= lastCompilerType; c++)
659                if(!strcmpi(value, compilerTypeNames[c]))
660                   return c;
661          }
662          return gcc;
663       }
664    };
665
666    property char * longName { get { return OnGetString(null, (void*)1, null); } };
667    property char * versionString { get { return OnGetString(null, (void*)2, null); } };
668    property char * yearString { get { return OnGetString(null, (void*)3, null); } };
669    property char * projectFileExtension { get { return OnGetString(null, (void*)4, null); } };
670    property char * solutionFileVersionString { get { return OnGetString(null, (void*)5, null); } };
671
672    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
673    {
674       if(this >= firstCompilerType && this <= lastCompilerType)
675       {
676          if(tempString)
677             strcpy(tempString, compilerTypeNames[this]);
678          if(fieldData == null)
679             return compilerTypeNames[this];
680          else if(fieldData == (void*)1)
681             return compilerTypeLongNames[this];
682          else if(fieldData == (void*)2)
683             return compilerTypeVersionString[this];
684          else if(fieldData == (void*)3)
685             return compilerTypeYearString[this];
686          else if(fieldData == (void*)4)
687             return compilerTypeProjectFileExtension[this];
688          else if(fieldData == (void*)5)
689             return compilerTypeSolutionFileVersionString[this];
690       }
691       return null;
692    }
693 };
694
695 class CompilerConfig
696 {
697    class_no_expansion;
698
699    numJobs = 1;
700 public:
701    property char * name
702    {
703       set
704       {
705          delete name;
706          if(value)
707             name = CopyString(value);
708       }
709       get { return name; }
710    }
711    bool readOnly;
712    CompilerType type;
713    Platform targetPlatform;
714    int numJobs;
715    property char * makeCommand
716    {
717       set { delete makeCommand; if(value && value[0]) makeCommand = CopyString(value); }
718       get { return makeCommand; }
719       isset { return makeCommand && makeCommand[0]; }
720    }
721    property char * ecpCommand
722    {
723       set { delete ecpCommand; if(value && value[0]) ecpCommand = CopyString(value); }
724       get { return ecpCommand; }
725       isset { return ecpCommand && ecpCommand[0]; }
726    }
727    property char * eccCommand
728    {
729       set { delete eccCommand; if(value && value[0]) eccCommand = CopyString(value); }
730       get { return eccCommand; }
731       isset { return eccCommand && eccCommand[0]; }
732    }
733    property char * ecsCommand
734    {
735       set { delete ecsCommand; if(value && value[0]) ecsCommand = CopyString(value); }
736       get { return ecsCommand; }
737       isset { return ecsCommand && ecsCommand[0]; }
738    }
739    property char * earCommand
740    {
741       set { delete earCommand; if(value && value[0]) earCommand = CopyString(value); }
742       get { return earCommand; }
743       isset { return earCommand && earCommand[0]; }
744    }
745    property char * cppCommand
746    {
747       set { delete cppCommand; if(value && value[0]) cppCommand = CopyString(value); }
748       get { return cppCommand; }
749       isset { return cppCommand && cppCommand[0]; }
750    }
751    property char * ccCommand
752    {
753       set { delete ccCommand; if(value && value[0]) ccCommand = CopyString(value); }
754       get { return ccCommand; }
755       isset { return ccCommand && ccCommand[0]; }
756    }
757    property char * cxxCommand
758    {
759       set { delete cxxCommand; if(value && value[0]) cxxCommand = CopyString(value); }
760       get { return cxxCommand; }
761       isset { return cxxCommand && cxxCommand[0]; }
762    }
763    property char * execPrefixCommand // <-- old name for json ide settings file compatibility
764    {
765       set { delete executableLauncher; if(value && value[0]) executableLauncher = CopyString(value); }
766       get { return executableLauncher; }
767       isset { return executableLauncher && executableLauncher[0]; }
768    }
769    // TODO: implement CompilerConfig::windresCommand
770    bool ccacheEnabled;
771    bool distccEnabled;
772    // deprecated
773    property bool supportsBitDepth { set { } get { return true; } isset { return false; } }
774
775    property char * distccHosts
776    {
777       set { delete distccHosts; if(value && value[0]) distccHosts = CopyString(value); }
778       get { return distccHosts; }
779       isset { return distccHosts && distccHosts[0]; }
780    }
781    property char * gccPrefix // <-- old name for json ide settings file compatibility
782    {
783       set { delete gnuToolchainPrefix; if(value && value[0]) gnuToolchainPrefix = CopyString(value); }
784       get { return gnuToolchainPrefix; }
785       isset { return gnuToolchainPrefix && gnuToolchainPrefix[0]; }
786    }
787    property char * sysroot
788    {
789       set { delete sysroot; if(value && value[0]) sysroot = CopyString(value); }
790       get { return sysroot; }
791       isset { return sysroot && sysroot[0]; }
792    }
793    property Array<String> includeDirs
794    {
795       set
796       {
797          includeDirs.Free();
798          if(value)
799          {
800             delete includeDirs;
801             includeDirs = value;
802          }
803       }
804       get { return includeDirs; }
805       isset { return includeDirs.count != 0; }
806    }
807    property Array<String> libraryDirs
808    {
809       set
810       {
811          libraryDirs.Free();
812          if(value)
813          {
814             delete libraryDirs;
815             libraryDirs = value;
816          }
817       }
818       get { return libraryDirs; }
819       isset { return libraryDirs.count != 0; }
820    }
821    property Array<String> executableDirs
822    {
823       set
824       {
825          executableDirs.Free();
826          if(value)
827          {
828             delete executableDirs;
829             executableDirs = value;
830          }
831       }
832       get { return executableDirs; }
833       isset { return executableDirs.count != 0; }
834    }
835    property Array<NamedString> environmentVars
836    {
837       set
838       {
839          environmentVars.Free();
840          if(value)
841          {
842             delete environmentVars;
843             environmentVars = value;
844          }
845       }
846       get { return environmentVars; }
847       isset { return environmentVars.count != 0; }
848    }
849    property Array<String> prepDirectives
850    {
851       set
852       {
853          prepDirectives.Free();
854          if(value)
855          {
856             delete prepDirectives;
857             prepDirectives = value;
858          }
859       }
860       get { return prepDirectives; }
861       isset { return prepDirectives.count != 0; }
862    }
863    property Array<String> excludeLibs
864    {
865       set
866       {
867          excludeLibs.Free();
868          if(value)
869          {
870             delete excludeLibs;
871             excludeLibs = value;
872          }
873       }
874       get { return excludeLibs; }
875       isset { return excludeLibs.count != 0; }
876    }
877    property Array<String> compilerFlags
878    {
879       set
880       {
881          compilerFlags.Free();
882          if(value)
883          {
884             delete compilerFlags;
885             compilerFlags = value;
886          }
887       }
888       get { return compilerFlags; }
889       isset { return compilerFlags.count != 0; }
890    }
891    property Array<String> linkerFlags
892    {
893       set
894       {
895          linkerFlags.Free();
896          if(value)
897          {
898             delete linkerFlags;
899             linkerFlags = value;
900          }
901       }
902       get { return linkerFlags; }
903       isset { return linkerFlags.count != 0; }
904    }
905 private:
906    Array<String> includeDirs { };
907    Array<String> libraryDirs { };
908    Array<String> executableDirs { };
909    // TODO: Can JSON parse and serialize maps?
910    //EnvironmentVariables { };
911    Array<NamedString> environmentVars { };
912    Array<String> prepDirectives { };
913    Array<String> excludeLibs { };
914    Array<String> compilerFlags { };
915    Array<String> linkerFlags { };
916    char * name;
917    char * makeCommand;
918    char * ecpCommand;
919    char * eccCommand;
920    char * ecsCommand;
921    char * earCommand;
922    char * cppCommand;
923    char * ccCommand;
924    char * cxxCommand;
925    char * executableLauncher;
926    char * distccHosts;
927    char * gnuToolchainPrefix;
928    char * sysroot;
929    /*union
930    {
931       struct { Array<String> includes, libraries, executables; };
932       Array<String> dirs[DirTypes];
933    }*/
934
935    ~CompilerConfig()
936    {
937       delete name;
938       delete ecpCommand;
939       delete eccCommand;
940       delete ecsCommand;
941       delete earCommand;
942       delete cppCommand;
943       delete ccCommand;
944       delete cxxCommand;
945       delete makeCommand;
946       delete executableLauncher;
947       delete distccHosts;
948       delete gnuToolchainPrefix;
949       delete sysroot;
950       if(environmentVars) environmentVars.Free();
951       if(includeDirs) { includeDirs.Free(); }
952       if(libraryDirs) { libraryDirs.Free(); }
953       if(executableDirs) { executableDirs.Free(); }
954       if(prepDirectives) { prepDirectives.Free(); }
955       if(excludeLibs) { excludeLibs.Free(); }
956       if(compilerFlags) { compilerFlags.Free(); }
957       if(linkerFlags) { linkerFlags.Free(); }
958    }
959    CompilerConfig Copy()
960    {
961       CompilerConfig copy
962       {
963          name,
964          readOnly,
965          type,
966          targetPlatform,
967          numJobs,
968          makeCommand,
969          ecpCommand,
970          eccCommand,
971          ecsCommand,
972          earCommand,
973          cppCommand,
974          ccCommand,
975          cxxCommand,
976          executableLauncher,
977          ccacheEnabled,
978          distccEnabled,
979          false,
980          distccHosts,
981          gnuToolchainPrefix,
982          sysroot
983       };
984       for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
985       for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
986       for(s : executableDirs) copy.executableDirs.Add(CopyString(s));
987       for(ns : environmentVars) copy.environmentVars.Add(NamedString { name = ns.name, string = ns.string });
988       for(s : prepDirectives) copy.prepDirectives.Add(CopyString(s));
989       for(s : excludeLibs) copy.excludeLibs.Add(CopyString(s));
990       for(s : compilerFlags) copy.compilerFlags.Add(CopyString(s));
991       for(s : linkerFlags) copy.linkerFlags.Add(CopyString(s));
992
993       incref copy;
994       return copy;
995    }
996 }