ide/Project: Added support for Linker Flags in the Compilers Configurations
[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,                 7         };
73    const char * vars[] = { "$(MODULE)", "$(CONFIG)", "$(PLATFORM)", "$(COMPILER)", "$(TARGET)", "$(COMPILER_SUFFIX)", "$(DEBUG_SUFFIX)", "$(PROJECT)",  "$(CONFIGURATION)",(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    void 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();
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             incref compiler;
289          }
290       }
291       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
292          data.ManagePortablePaths(moduleLocation, true);
293       data.ForcePathSeparatorStyle(true);
294       OnLoad(data);
295    }
296
297    void Save()
298    {
299       IDESettings data = (IDESettings)this.data;
300       Platform runtimePlatform = GetRuntimePlatform();
301       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
302          data.ManagePortablePaths(moduleLocation, false);
303       data.ForcePathSeparatorStyle(true);
304       if(!GlobalSettings::Save())
305          PrintLn("Error saving IDE settings");
306       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
307          data.ManagePortablePaths(moduleLocation, true);
308       CloseAndMonitor();
309    }
310 }
311
312 class IDESettings : GlobalSettingsData
313 {
314 public:
315    List<CompilerConfig> compilerConfigs { };
316    Array<String> recentFiles { };
317    Array<String> recentProjects { };
318    property char * docDir
319    {
320       set { delete docDir; if(value && value[0]) docDir = CopyString(value); }
321       get { return docDir ? docDir : ""; }
322       isset { return docDir && docDir[0]; }
323    }
324    property char * ideFileDialogLocation
325    {
326       set { delete ideFileDialogLocation; if(value && value[0]) ideFileDialogLocation = CopyString(value); }
327       get { return ideFileDialogLocation ? ideFileDialogLocation : ""; }
328       isset { return ideFileDialogLocation && ideFileDialogLocation[0]; }
329    }
330    property char * ideProjectFileDialogLocation
331    {
332       set { delete ideProjectFileDialogLocation; if(value && value[0]) ideProjectFileDialogLocation = CopyString(value); }
333       get { return ideProjectFileDialogLocation ? ideProjectFileDialogLocation : ""; }
334       isset { return ideProjectFileDialogLocation && ideProjectFileDialogLocation[0]; }
335    }
336    bool useFreeCaret;
337    bool showLineNumbers;
338    bool caretFollowsScrolling;
339    char * displayDriver;
340    
341    // TODO: Classify settings
342    //EditorSettings editor { };
343
344    property char * projectDefaultTargetDir
345    {
346       set { delete projectDefaultTargetDir; if(value && value[0]) projectDefaultTargetDir = CopyValidateMakefilePath(value); }
347       get { return projectDefaultTargetDir ? projectDefaultTargetDir : ""; }
348       isset { return projectDefaultTargetDir && projectDefaultTargetDir[0]; }
349    }
350    property char * projectDefaultIntermediateObjDir
351    {
352       set { delete projectDefaultIntermediateObjDir; if(value && value[0]) projectDefaultIntermediateObjDir = CopyValidateMakefilePath(value); }
353       get { return projectDefaultIntermediateObjDir ? projectDefaultIntermediateObjDir : ""; }
354       isset { return projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0]; }
355    }
356
357    property char * compilerConfigsDir
358    {
359       set { delete compilerConfigsDir; if(value && value[0]) compilerConfigsDir = CopyString(value); }
360       get { return compilerConfigsDir ? compilerConfigsDir : ""; }
361       isset { return compilerConfigsDir && compilerConfigsDir[0]; }
362    }
363
364    property char * defaultCompiler
365    {
366       set { delete defaultCompiler; if(value && value[0]) defaultCompiler = CopyString(value); }
367       get { return defaultCompiler && defaultCompiler[0] ? defaultCompiler : defaultCompilerName; }
368       isset { return defaultCompiler && defaultCompiler[0]; }
369    }
370
371 private:
372    char * docDir;
373    char * ideFileDialogLocation;
374    char * ideProjectFileDialogLocation;
375    char * projectDefaultTargetDir;
376    char * projectDefaultIntermediateObjDir;
377    char * compilerConfigsDir;
378    char * defaultCompiler;
379
380    CompilerConfig GetCompilerConfig(String compilerName)
381    {
382       char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
383       CompilerConfig compilerConfig = null;
384       for(compiler : compilerConfigs)
385       {
386          if(!strcmp(compiler.name, name))
387          {
388             compilerConfig = compiler;
389             break;
390          }
391       }
392       if(!compilerConfig && compilerConfigs.count)
393          compilerConfig = compilerConfigs.firstIterator.data;
394       if(compilerConfig)
395          incref compilerConfig;
396       return compilerConfig;
397    }
398
399    ~IDESettings()
400    {
401       compilerConfigs.Free();
402       delete compilerConfigs;
403       recentFiles.Free();
404       delete recentFiles;
405       recentProjects.Free();
406       delete recentProjects;
407       delete docDir;
408    
409       delete projectDefaultTargetDir;
410       delete projectDefaultIntermediateObjDir;
411       delete compilerConfigsDir;
412       delete defaultCompiler;
413
414       delete ideFileDialogLocation;
415       delete ideProjectFileDialogLocation;
416       delete displayDriver;
417    }
418
419    void ForcePathSeparatorStyle(bool unixStyle)
420    {
421       char from, to;
422       if(unixStyle)
423          from = '\\', to = '/';
424       else
425          from = '/', to = '\\';
426       if(compilerConfigs && compilerConfigs.count)
427       {
428          int i;
429          for(config : compilerConfigs)
430          {
431             if(config.includeDirs && config.includeDirs.count)
432             {
433                for(i = 0; i < config.includeDirs.count; i++)
434                {
435                   if(config.includeDirs[i] && config.includeDirs[i][0])
436                      ChangeCh(config.includeDirs[i], from, to);
437                }
438             }
439             if(config.libraryDirs && config.libraryDirs.count)
440             {
441                for(i = 0; i < config.libraryDirs.count; i++)
442                {
443                   if(config.libraryDirs[i] && config.libraryDirs[i][0])
444                      ChangeCh(config.libraryDirs[i], from, to);
445                }
446             }
447             if(config.executableDirs && config.executableDirs.count)
448             {
449                for(i = 0; i < config.executableDirs.count; i++)
450                {
451                   if(config.executableDirs[i] && config.executableDirs[i][0])
452                      ChangeCh(config.executableDirs[i], from, to);
453                }
454             }
455          }
456       }
457       if(recentFiles && recentFiles.count)
458       {
459          int c;
460          for(c = 0; c < recentFiles.count; c++)
461          {
462             if(recentFiles[c] && recentFiles[c][0])
463                ChangeCh(recentFiles[c], from, to);
464          }
465       }
466       if(recentProjects && recentProjects.count)
467       {
468          int c;
469          for(c = 0; c < recentProjects.count; c++)
470          {
471             if(recentProjects[c] && recentProjects[c][0])
472                ChangeCh(recentProjects[c], from, to);
473          }
474       }
475       if(docDir && docDir[0])
476          ChangeCh(docDir, from, to);
477       if(ideFileDialogLocation && ideFileDialogLocation[0])
478          ChangeCh(ideFileDialogLocation, from, to);
479       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
480          ChangeCh(ideProjectFileDialogLocation, from, to);
481
482       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
483          ChangeCh(projectDefaultTargetDir, from, to);
484       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
485          ChangeCh(projectDefaultIntermediateObjDir, from, to);
486
487       if(compilerConfigsDir && compilerConfigsDir[0])
488          ChangeCh(compilerConfigsDir, from, to);
489    }
490
491    void ManagePortablePaths(char * location, bool makeAbsolute)
492    {
493       int c;
494       if(compilerConfigs && compilerConfigs.count)
495       {
496          for(config : compilerConfigs)
497          {
498             DirTypes t;
499             for(t = 0; t < DirTypes::enumSize; t++)
500             {
501                Array<String> dirs = null;
502                if(t == executables) dirs = config.executableDirs;
503                else if(t == includes) dirs = config.includeDirs;
504                else if(t == libraries) dirs = config.libraryDirs;
505                if(dirs && dirs.count)
506                {
507                   for(c = 0; c < dirs.count; c++)
508                   {
509                      if(dirs[c] && dirs[c][0])
510                         dirs[c] = UpdatePortablePath(dirs[c], location, makeAbsolute);
511                   }
512                }
513             }
514          }
515       }
516       if(recentFiles && recentFiles.count)
517       {
518          for(c = 0; c < recentFiles.count; c++)
519          {
520             if(recentFiles[c] && recentFiles[c][0])
521                recentFiles[c] = UpdatePortablePath(recentFiles[c], location, makeAbsolute);
522          }
523       }
524       if(recentProjects && recentProjects.count)
525       {
526          for(c = 0; c < recentProjects.count; c++)
527          {
528             if(recentProjects[c] && recentProjects[c][0])
529                recentProjects[c] = UpdatePortablePath(recentProjects[c], location, makeAbsolute);
530          }
531       }
532       if(docDir && docDir[0])
533          docDir = UpdatePortablePath(docDir, location, makeAbsolute);
534       if(ideFileDialogLocation && ideFileDialogLocation[0])
535          ideFileDialogLocation = UpdatePortablePath(ideFileDialogLocation, location, makeAbsolute);
536       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
537          ideProjectFileDialogLocation = UpdatePortablePath(ideProjectFileDialogLocation, location, makeAbsolute);
538
539       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
540          projectDefaultTargetDir = UpdatePortablePath(projectDefaultTargetDir, location, makeAbsolute);
541       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
542          projectDefaultIntermediateObjDir = UpdatePortablePath(projectDefaultIntermediateObjDir, location, makeAbsolute);
543
544       if(compilerConfigsDir && compilerConfigsDir[0])
545          compilerConfigsDir = UpdatePortablePath(compilerConfigsDir, location, makeAbsolute);
546    }
547
548    char * UpdatePortablePath(char * path, char * location, bool makeAbsolute)
549    {
550       char * output;
551       if(makeAbsolute)
552       {
553          char p[MAX_LOCATION];
554          strcpy(p, location);
555          PathCatSlash(p, path);
556          delete path;
557          output = CopyString(p);
558       }
559       else
560       {
561          PathRelationship rel = eString_PathRelated(path, location, null);
562          if(rel == subPath || rel == identical)
563          {
564             char p[MAX_LOCATION];
565             MakePathRelative(path, location, p);
566             if(!*p) strcpy(p, "./");
567             else ChangeCh(p, '\\', '/');
568             delete path;
569             output = CopyString(p);
570          }
571          else
572             output = path;
573       }
574       return output;
575    }
576
577    void AddRecentFile(char * fileName)
578    {
579       int c;
580       char * filePath = CopyString(fileName);
581       ChangeCh(filePath, '\\', '/');
582       for(c = 0; c<recentFiles.count; c++)
583       {
584          if(recentFiles[c] && !fstrcmp(recentFiles[c], filePath))
585          {
586             recentFiles.Delete((void *)&recentFiles[c]);
587             c--;
588          }
589       }
590       while(recentFiles.count >= MaxRecent)
591          recentFiles.Delete(recentFiles.GetLast());
592       recentFiles.Insert(null, filePath);
593       //UpdateRecentMenus(owner);
594    }
595
596    void AddRecentProject(char * projectName)
597    {
598       int c;
599       char * filePath = CopyString(projectName);
600       ChangeCh(filePath, '\\', '/');
601       for(c = 0; c<recentProjects.count; c++)
602       {
603          if(recentProjects[c] && !fstrcmp(recentProjects[c], filePath))
604          {
605             recentProjects.Delete((void *)&recentProjects[c]);
606             c--;
607          }
608       }
609       while(recentProjects.count >= MaxRecent)
610          recentProjects.Delete(recentProjects.GetLast());
611       recentProjects.Insert(null, filePath);
612       //UpdateRecentMenus(owner);
613    }
614 }
615
616 static const char * compilerTypeNames[CompilerType] = { "GCC", "TCC", "PCC", "VS8", "VS9", "VS10" };
617 static const char * compilerTypeVersionString[CompilerType] = { "", "", "", "8.00", "9.00", "10.00" };
618 static const char * compilerTypeSolutionFileVersionString[CompilerType] = { "", "", "", "9.00", "10.00", "11.00" };
619 static const char * compilerTypeYearString[CompilerType] = { "", "", "", "2005", "2008", "2010" };
620 static const char * compilerTypeProjectFileExtension[CompilerType] = { "", "", "", "vcproj", "vcproj", "vcxproj" };
621 // TODO: i18n with Array
622 static Array<String> compilerTypeLongNames
623 { [
624    $"GNU Compiler Collection (GCC) / GNU Make",
625    $"Tiny C Compiler / GNU Make",
626    $"Portable C Compiler / GNU Make",
627    $"Microsoft Visual Studio 2005 (8.0) Compiler",
628    $"Microsoft Visual Studio 2008 (9.0) Compiler",
629    $"Microsoft Visual Studio 2010 (10.0) Compiler"
630 ] };
631 const CompilerType firstCompilerType = gcc;
632 const CompilerType lastCompilerType = vs10;
633 public enum CompilerType
634 {
635    gcc, tcc, pcc, vs8, vs9, vs10;
636
637    property bool isVC
638    {
639       get { return this == vs8 || this == vs9 || this == vs10; }
640    }
641
642    property char *
643    {
644       get { return OnGetString(null, null, null); }
645       set
646       {  
647          if(value)
648          {
649             Platform c;
650             for(c = firstCompilerType; c <= lastCompilerType; c++)
651                if(!strcmpi(value, compilerTypeNames[c]))
652                   return c;
653          }
654          return gcc;
655       }
656    };
657
658    property char * longName { get { return OnGetString(null, (void*)1, null); } };
659    property char * versionString { get { return OnGetString(null, (void*)2, null); } };
660    property char * yearString { get { return OnGetString(null, (void*)3, null); } };
661    property char * projectFileExtension { get { return OnGetString(null, (void*)4, null); } };
662    property char * solutionFileVersionString { get { return OnGetString(null, (void*)5, null); } };
663
664    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
665    {
666       if(this >= firstCompilerType && this <= lastCompilerType)
667       {
668          if(tempString)
669             strcpy(tempString, compilerTypeNames[this]);
670          if(fieldData == null)
671             return compilerTypeNames[this];
672          else if(fieldData == (void*)1)
673             return compilerTypeLongNames[this];
674          else if(fieldData == (void*)2)
675             return compilerTypeVersionString[this];
676          else if(fieldData == (void*)3)
677             return compilerTypeYearString[this];
678          else if(fieldData == (void*)4)
679             return compilerTypeProjectFileExtension[this];
680          else if(fieldData == (void*)5)
681             return compilerTypeSolutionFileVersionString[this];
682       }
683       return null;
684    }
685 };
686
687 class CompilerConfig
688 {
689    class_no_expansion;
690
691    numJobs = 1;
692    supportsBitDepth = true;
693 public:
694    property char * name
695    {
696       set
697       {
698          delete name;
699          if(value)
700             name = CopyString(value);
701       }
702       get { return name; }
703    }
704    bool readOnly;
705    CompilerType type;
706    Platform targetPlatform;
707    int numJobs;
708    property char * makeCommand
709    {
710       set { delete makeCommand; if(value && value[0]) makeCommand = CopyString(value); }
711       get { return makeCommand; }
712       isset { return makeCommand && makeCommand[0]; }
713    }
714    property char * ecpCommand
715    {
716       set { delete ecpCommand; if(value && value[0]) ecpCommand = CopyString(value); }
717       get { return ecpCommand; }
718       isset { return ecpCommand && ecpCommand[0]; }
719    }
720    property char * eccCommand
721    {
722       set { delete eccCommand; if(value && value[0]) eccCommand = CopyString(value); }
723       get { return eccCommand; }
724       isset { return eccCommand && eccCommand[0]; }
725    }
726    property char * ecsCommand
727    {
728       set { delete ecsCommand; if(value && value[0]) ecsCommand = CopyString(value); }
729       get { return ecsCommand; }
730       isset { return ecsCommand && ecsCommand[0]; }
731    }
732    property char * earCommand
733    {
734       set { delete earCommand; if(value && value[0]) earCommand = CopyString(value); }
735       get { return earCommand; }
736       isset { return earCommand && earCommand[0]; }
737    }
738    property char * cppCommand
739    {
740       set { delete cppCommand; if(value && value[0]) cppCommand = CopyString(value); }
741       get { return cppCommand; }
742       isset { return cppCommand && cppCommand[0]; }
743    }
744    property char * ccCommand
745    {
746       set { delete ccCommand; if(value && value[0]) ccCommand = CopyString(value); }
747       get { return ccCommand; }
748       isset { return ccCommand && ccCommand[0]; }
749    }
750    property char * cxxCommand
751    {
752       set { delete cxxCommand; if(value && value[0]) cxxCommand = CopyString(value); }
753       get { return cxxCommand; }
754       isset { return cxxCommand && cxxCommand[0]; }
755    }
756    property char * execPrefixCommand
757    {
758       set { delete execPrefixCommand; if(value && value[0]) execPrefixCommand = CopyString(value); }
759       get { return execPrefixCommand; }
760       isset { return execPrefixCommand && execPrefixCommand[0]; }
761    }
762    bool ccacheEnabled;
763    bool distccEnabled;
764    property bool supportsBitDepth
765    {
766       set { supportsBitDepth = value; }
767       get { return supportsBitDepth; }
768       isset { return !supportsBitDepth; }
769    }
770
771    property char * distccHosts
772    {
773       set { delete distccHosts; if(value && value[0]) distccHosts = CopyString(value); }
774       get { return distccHosts; }
775       isset { return distccHosts && distccHosts[0]; }
776    }
777    property Array<String> includeDirs
778    {
779       set
780       {
781          includeDirs.Free();
782          if(value)
783          {
784             delete includeDirs;
785             includeDirs = value;
786          }
787       }
788       get { return includeDirs; }
789       isset { return includeDirs.count != 0; }
790    }
791    property Array<String> libraryDirs
792    {
793       set
794       {
795          libraryDirs.Free();
796          if(value)
797          {
798             delete libraryDirs;
799             libraryDirs = value;
800          }
801       }
802       get { return libraryDirs; }
803       isset { return libraryDirs.count != 0; }
804    }
805    property Array<String> executableDirs
806    {
807       set
808       {
809          executableDirs.Free();
810          if(value)
811          {
812             delete executableDirs;
813             executableDirs = value;
814          }
815       }
816       get { return executableDirs; }
817       isset { return executableDirs.count != 0; }
818    }
819    property Array<NamedString> environmentVars
820    {
821       set
822       {
823          environmentVars.Free();
824          if(value)
825          {
826             delete environmentVars;
827             environmentVars = value;
828          }
829       }
830       get { return environmentVars; }
831       isset { return environmentVars.count != 0; }
832    }
833    property Array<String> prepDirectives
834    {
835       set
836       {
837          prepDirectives.Free();
838          if(value)
839          {
840             delete prepDirectives;
841             prepDirectives = value;
842          }
843       }
844       get { return prepDirectives; }
845       isset { return prepDirectives.count != 0; }
846    }
847    property Array<String> excludeLibs
848    {
849       set
850       {
851          excludeLibs.Free();
852          if(value)
853          {
854             delete excludeLibs;
855             excludeLibs = value;
856          }
857       }
858       get { return excludeLibs; }
859       isset { return excludeLibs.count != 0; }
860    }
861    property Array<String> linkerFlags
862    {
863       set
864       {
865          linkerFlags.Free();
866          if(value)
867          {
868             delete linkerFlags;
869             linkerFlags = value;
870          }
871       }
872       get { return linkerFlags; }
873       isset { return linkerFlags.count != 0; }
874    }
875 private:
876    Array<String> includeDirs { };
877    Array<String> libraryDirs { };
878    Array<String> executableDirs { };
879    // TODO: Can JSON parse and serialize maps?
880    //EnvironmentVariables { };
881    Array<NamedString> environmentVars { };
882    Array<String> prepDirectives { };
883    Array<String> excludeLibs { };
884    Array<String> linkerFlags { };
885    char * name;
886    char * makeCommand;
887    char * ecpCommand;
888    char * eccCommand;
889    char * ecsCommand;
890    char * earCommand;
891    char * cppCommand;
892    char * ccCommand;
893    char * cxxCommand;
894    char * execPrefixCommand;
895    char * distccHosts;
896    bool supportsBitDepth;
897    /*union
898    {
899       struct { Array<String> includes, libraries, executables; };
900       Array<String> dirs[DirTypes];
901    }*/
902
903    ~CompilerConfig()
904    {
905       delete name;
906       delete ecpCommand;
907       delete eccCommand;
908       delete ecsCommand;
909       delete earCommand;
910       delete cppCommand;
911       delete ccCommand;
912       delete cxxCommand;
913       delete makeCommand;
914       delete execPrefixCommand;
915       delete distccHosts;
916       if(environmentVars) environmentVars.Free();
917       if(includeDirs) { includeDirs.Free(); }
918       if(libraryDirs) { libraryDirs.Free(); }
919       if(executableDirs) { executableDirs.Free(); }
920       if(prepDirectives) { prepDirectives.Free(); }
921       if(excludeLibs) { excludeLibs.Free(); }
922       if(linkerFlags) { linkerFlags.Free(); }
923    }
924    CompilerConfig Copy()
925    {
926       CompilerConfig copy
927       {
928          name,
929          readOnly,
930          type,
931          targetPlatform,
932          numJobs,
933          makeCommand,
934          ecpCommand,
935          eccCommand,
936          ecsCommand,
937          earCommand,
938          cppCommand,
939          ccCommand,
940          cxxCommand,
941          execPrefixCommand,
942          ccacheEnabled,
943          distccEnabled,
944          supportsBitDepth,
945          distccHosts
946       };
947       for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
948       for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
949       for(s : executableDirs) copy.executableDirs.Add(CopyString(s));
950       for(ns : environmentVars) copy.environmentVars.Add(NamedString { name = ns.name, string = ns.string });
951       for(s : prepDirectives) copy.prepDirectives.Add(CopyString(s));
952       for(s : excludeLibs) copy.excludeLibs.Add(CopyString(s));
953       for(s : linkerFlags) copy.linkerFlags.Add(CopyString(s));
954
955       incref copy;
956       return copy;
957    }
958 }