ide/Project: Added a distinct C++ compiling/linking command (default to g++) to work...
[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 public:
693    property char * name
694    {
695       set
696       {
697          delete name;
698          if(value)
699             name = CopyString(value);
700       }
701       get { return name; }
702    }
703    bool readOnly;
704    CompilerType type;
705    Platform targetPlatform;
706    int numJobs;
707    property char * makeCommand
708    {
709       set { delete makeCommand; if(value && value[0]) makeCommand = CopyString(value); }
710       get { return makeCommand; }
711       isset { return makeCommand && makeCommand[0]; }
712    }
713    property char * ecpCommand
714    {
715       set { delete ecpCommand; if(value && value[0]) ecpCommand = CopyString(value); }
716       get { return ecpCommand; }
717       isset { return ecpCommand && ecpCommand[0]; }
718    }
719    property char * eccCommand
720    {
721       set { delete eccCommand; if(value && value[0]) eccCommand = CopyString(value); }
722       get { return eccCommand; }
723       isset { return eccCommand && eccCommand[0]; }
724    }
725    property char * ecsCommand
726    {
727       set { delete ecsCommand; if(value && value[0]) ecsCommand = CopyString(value); }
728       get { return ecsCommand; }
729       isset { return ecsCommand && ecsCommand[0]; }
730    }
731    property char * earCommand
732    {
733       set { delete earCommand; if(value && value[0]) earCommand = CopyString(value); }
734       get { return earCommand; }
735       isset { return earCommand && earCommand[0]; }
736    }
737    property char * cppCommand
738    {
739       set { delete cppCommand; if(value && value[0]) cppCommand = CopyString(value); }
740       get { return cppCommand; }
741       isset { return cppCommand && cppCommand[0]; }
742    }
743    property char * ccCommand
744    {
745       set { delete ccCommand; if(value && value[0]) ccCommand = CopyString(value); }
746       get { return ccCommand; }
747       isset { return ccCommand && ccCommand[0]; }
748    }
749    property char * cxxCommand
750    {
751       set { delete cxxCommand; if(value && value[0]) cxxCommand = CopyString(value); }
752       get { return cxxCommand; }
753       isset { return cxxCommand && cxxCommand[0]; }
754    }
755    property char * execPrefixCommand
756    {
757       set { delete execPrefixCommand; if(value && value[0]) execPrefixCommand = CopyString(value); }
758       get { return execPrefixCommand; }
759       isset { return execPrefixCommand && execPrefixCommand[0]; }
760    }
761    bool ccacheEnabled;
762    bool distccEnabled;
763    property char * distccHosts
764    {
765       set { delete distccHosts; if(value && value[0]) distccHosts = CopyString(value); }
766       get { return distccHosts; }
767       isset { return distccHosts && distccHosts[0]; }
768    }
769    property Array<String> includeDirs
770    {
771       set
772       {
773          includeDirs.Free();
774          if(value)
775          {
776             delete includeDirs;
777             includeDirs = value;
778          }
779       }
780       get { return includeDirs; }
781       isset { return includeDirs.count != 0; }
782    }
783    property Array<String> libraryDirs
784    {
785       set
786       {
787          libraryDirs.Free();
788          if(value)
789          {
790             delete libraryDirs;
791             libraryDirs = value;
792          }
793       }
794       get { return libraryDirs; }
795       isset { return libraryDirs.count != 0; }
796    }
797    property Array<String> executableDirs
798    {
799       set
800       {
801          executableDirs.Free();
802          if(value)
803          {
804             delete executableDirs;
805             executableDirs = value;
806          }
807       }
808       get { return executableDirs; }
809       isset { return executableDirs.count != 0; }
810    }
811    property Array<NamedString> environmentVars
812    {
813       set
814       {
815          environmentVars.Free();
816          if(value)
817          {
818             delete environmentVars;
819             environmentVars = value;
820          }
821       }
822       get { return environmentVars; }
823       isset { return environmentVars.count != 0; }
824    }
825 private:
826    Array<String> includeDirs { };
827    Array<String> libraryDirs { };
828    Array<String> executableDirs { };
829    // TODO: Can JSON parse and serialize maps?
830    //EnvironmentVariables { };
831    Array<NamedString> environmentVars { };
832    char * name;
833    char * makeCommand;
834    char * ecpCommand;
835    char * eccCommand;
836    char * ecsCommand;
837    char * earCommand;
838    char * cppCommand;
839    char * ccCommand;
840    char * cxxCommand;
841    char * execPrefixCommand;
842    char * distccHosts;
843    /*union
844    {
845       struct { Array<String> includes, libraries, executables; };
846       Array<String> dirs[DirTypes];
847    }*/
848
849    ~CompilerConfig()
850    {
851       delete name;
852       delete ecpCommand;
853       delete eccCommand;
854       delete ecsCommand;
855       delete earCommand;
856       delete cppCommand;
857       delete ccCommand;
858       delete cxxCommand;
859       delete makeCommand;
860       delete execPrefixCommand;
861       delete distccHosts;
862       if(environmentVars) environmentVars.Free();
863       if(includeDirs) { includeDirs.Free(); }
864       if(libraryDirs) { libraryDirs.Free(); }
865       if(executableDirs) { executableDirs.Free(); }
866    }
867    CompilerConfig Copy()
868    {
869       CompilerConfig copy
870       {
871          name,
872          readOnly,
873          type,
874          targetPlatform,
875          numJobs,
876          makeCommand,
877          ecpCommand,
878          eccCommand,
879          ecsCommand,
880          earCommand,
881          cppCommand,
882          ccCommand,
883          cxxCommand,
884          execPrefixCommand,
885          ccacheEnabled,
886          distccEnabled,
887          distccHosts
888       };
889       for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
890       for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
891       for(s : executableDirs) copy.executableDirs.Add(CopyString(s));
892       for(ns : environmentVars) copy.environmentVars.Add(NamedString { name = ns.name, string = ns.string });
893
894       incref copy;
895       return copy;
896    }
897 }