ide: generate a single makefile, no more compiler specific makefile. added compiler...
[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)";
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,           0,            1,                  7         };
73    const char * vars[] = { "$(MODULE)", "$(CONFIG)", "$(PLATFORM)", "$(COMPILER)", "$(TARGET)", "$(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    };
145    incref defaultCompiler;
146    return defaultCompiler;
147 }
148
149 class IDESettingsContainer : GlobalSettings
150 {
151 #ifdef SETTINGS_TEST
152    settingsName = "ecereIDESettingsTest";
153 #else
154    settingsName = "ecereIDE";
155 #endif
156
157    virtual void OnLoad(GlobalSettingsData data);
158
159    char moduleLocation[MAX_LOCATION];
160
161 private:
162    IDESettingsContainer()
163    {
164       char path[MAX_LOCATION];
165       char * start;
166       LocateModule(null, moduleLocation);
167       strcpy(path, moduleLocation);
168       StripLastDirectory(moduleLocation, moduleLocation);
169       ChangeCh(moduleLocation, '\\', '/');
170       // PortableApps.com directory structure
171       if((start = strstr(path, "\\App\\EcereSDK\\bin\\ide.exe")))
172       {
173          char configFilePath[MAX_LOCATION];
174          char defaultConfigFilePath[MAX_LOCATION];
175
176          start[0] = '\0';
177
178          strcpy(configFilePath, path);
179          PathCat(configFilePath, "Data");
180          PathCat(configFilePath, "ecereIDE.ini");
181
182          strcpy(defaultConfigFilePath, path);
183          PathCat(defaultConfigFilePath, "App");
184          PathCat(defaultConfigFilePath, "DefaultData");
185          PathCat(defaultConfigFilePath, "ecereIDE.ini");
186          
187          if(FileExists(defaultConfigFilePath))
188          {
189             if(!FileExists(configFilePath))
190             {
191                File f = FileOpen(defaultConfigFilePath, read);
192                f.CopyTo(configFilePath);
193                f.Flush();
194                delete f;
195             }
196             PathCat(path, "Data");
197             // the forced settings location will only be
198             // used if the running ide's path matches 
199             // the PortableApps.com directory structure
200             // and the default ini file is found in 
201             // the DefaultData directory
202             settingsLocation = path;
203             portable = true;
204          }
205       }
206    }
207
208    void OnAskReloadSettings()
209    {
210       /*if(MessageBox { type = YesNo, master = this, 
211             text = "Global Settings Modified Externally", 
212             contents = "The global settings were modified by another instance.\n"
213             "Would you like to reload them?" }.Modal() == Yes)*/
214       {
215          Load();
216       }
217    }
218
219    void Load()
220    {
221       SettingsIOResult result = GlobalSettings::Load();
222       IDESettings data = (IDESettings)this.data;
223       CompilerConfig defaultCompiler = null;
224       if(!data)
225       {
226          this.data = IDESettings { };
227          if(dataOwner)
228             *dataOwner = this.data;
229
230          if(result == fileNotCompatibleWithDriver)
231          {
232             bool loaded;
233             OldIDESettings oldSettings { };
234             Close();
235             loaded = oldSettings.Load();
236             oldSettings.Close();
237             if(loaded)
238             {
239                data = (IDESettings)this.data;
240
241                for(c : oldSettings.compilerConfigs)
242                   data.compilerConfigs.Add(c.Copy());
243
244                for(s : oldSettings.recentFiles) data.recentFiles.Add(CopyString(s));
245                for(s : oldSettings.recentProjects) data.recentProjects.Add(CopyString(s));
246
247                data.docDir = oldSettings.docDir;
248                data.ideFileDialogLocation = oldSettings.ideFileDialogLocation;
249                data.ideProjectFileDialogLocation = oldSettings.ideProjectFileDialogLocation;
250                data.useFreeCaret = oldSettings.useFreeCaret;
251                data.showLineNumbers = oldSettings.showLineNumbers;
252                data.caretFollowsScrolling = oldSettings.caretFollowsScrolling;
253                delete data.displayDriver; data.displayDriver = CopyString(oldSettings.displayDriver);
254                data.projectDefaultTargetDir = oldSettings.projectDefaultTargetDir;
255                data.projectDefaultIntermediateObjDir = oldSettings.projectDefaultIntermediateObjDir;
256                         
257                Save();
258                result = success;
259             }
260             delete oldSettings;
261          }
262          if(result == fileNotFound || !data)
263          {
264             data = (IDESettings)this.data;
265             data.useFreeCaret = true;
266             data.showLineNumbers = true;
267             data.caretFollowsScrolling = true;
268          }
269       }
270       // Ensure we have a default compiler
271       defaultCompiler = data.GetCompilerConfig(defaultCompilerName);
272       if(!defaultCompiler)
273       {
274          defaultCompiler = MakeDefaultCompiler(defaultCompilerName, true);
275          data.compilerConfigs.Add(defaultCompiler);
276       }
277       if(!data.compilerConfigsDir || !data.compilerConfigsDir[0])
278          data.property::compilerConfigsDir = "configs";
279
280       // We incref the compilers below, so reset refCount to 0
281       defaultCompiler._refCount = 0;
282
283       CloseAndMonitor();
284       if(data.compilerConfigs)
285       {
286          for(c : data.compilerConfigs)
287          {
288             CompilerConfig compiler = c;
289             incref compiler;
290          }
291       }
292       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
293          data.ManagePortablePaths(moduleLocation, true);
294       data.ForcePathSeparatorStyle(true);
295       OnLoad(data);
296    }
297
298    void Save()
299    {
300       IDESettings data = (IDESettings)this.data;
301       Platform runtimePlatform = GetRuntimePlatform();
302       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
303          data.ManagePortablePaths(moduleLocation, false);
304       data.ForcePathSeparatorStyle(true);
305       if(!GlobalSettings::Save())
306          PrintLn("Error saving IDE settings");
307       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
308          data.ManagePortablePaths(moduleLocation, true);
309       CloseAndMonitor();
310    }
311 }
312
313 class IDESettings : GlobalSettingsData
314 {
315 public:
316    List<CompilerConfig> compilerConfigs { };
317    Array<String> recentFiles { };
318    Array<String> recentProjects { };
319    property char * docDir
320    {
321       set { delete docDir; if(value && value[0]) docDir = CopyString(value); }
322       get { return docDir ? docDir : ""; }
323       isset { return docDir && docDir[0]; }
324    }
325    property char * ideFileDialogLocation
326    {
327       set { delete ideFileDialogLocation; if(value && value[0]) ideFileDialogLocation = CopyString(value); }
328       get { return ideFileDialogLocation ? ideFileDialogLocation : ""; }
329       isset { return ideFileDialogLocation && ideFileDialogLocation[0]; }
330    }
331    property char * ideProjectFileDialogLocation
332    {
333       set { delete ideProjectFileDialogLocation; if(value && value[0]) ideProjectFileDialogLocation = CopyString(value); }
334       get { return ideProjectFileDialogLocation ? ideProjectFileDialogLocation : ""; }
335       isset { return ideProjectFileDialogLocation && ideProjectFileDialogLocation[0]; }
336    }
337    bool useFreeCaret;
338    bool showLineNumbers;
339    bool caretFollowsScrolling;
340    char * displayDriver;
341    
342    // TODO: Classify settings
343    //EditorSettings editor { };
344
345    property char * projectDefaultTargetDir
346    {
347       set { delete projectDefaultTargetDir; if(value && value[0]) projectDefaultTargetDir = CopyValidateMakefilePath(value); }
348       get { return projectDefaultTargetDir ? projectDefaultTargetDir : ""; }
349       isset { return projectDefaultTargetDir && projectDefaultTargetDir[0]; }
350    }
351    property char * projectDefaultIntermediateObjDir
352    {
353       set { delete projectDefaultIntermediateObjDir; if(value && value[0]) projectDefaultIntermediateObjDir = CopyValidateMakefilePath(value); }
354       get { return projectDefaultIntermediateObjDir ? projectDefaultIntermediateObjDir : ""; }
355       isset { return projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0]; }
356    }
357
358    property char * compilerConfigsDir
359    {
360       set { delete compilerConfigsDir; if(value && value[0]) compilerConfigsDir = CopyString(value); }
361       get { return compilerConfigsDir ? compilerConfigsDir : ""; }
362       isset { return compilerConfigsDir && compilerConfigsDir[0]; }
363    }
364
365    property char * defaultCompiler
366    {
367       set { delete defaultCompiler; if(value && value[0]) defaultCompiler = CopyString(value); }
368       get { return defaultCompiler && defaultCompiler[0] ? defaultCompiler : defaultCompilerName; }
369       isset { return defaultCompiler && defaultCompiler[0]; }
370    }
371
372 private:
373    char * docDir;
374    char * ideFileDialogLocation;
375    char * ideProjectFileDialogLocation;
376    char * projectDefaultTargetDir;
377    char * projectDefaultIntermediateObjDir;
378    char * compilerConfigsDir;
379    char * defaultCompiler;
380
381    CompilerConfig GetCompilerConfig(String compilerName)
382    {
383       char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
384       CompilerConfig compilerConfig = null;
385       for(compiler : compilerConfigs)
386       {
387          if(!strcmp(compiler.name, name))
388          {
389             compilerConfig = compiler;
390             break;
391          }
392       }
393       if(!compilerConfig && compilerConfigs.count)
394          compilerConfig = compilerConfigs.firstIterator.data;
395       if(compilerConfig)
396          incref compilerConfig;
397       return compilerConfig;
398    }
399
400    ~IDESettings()
401    {
402       compilerConfigs.Free();
403       delete compilerConfigs;
404       recentFiles.Free();
405       delete recentFiles;
406       recentProjects.Free();
407       delete recentProjects;
408       delete docDir;
409    
410       delete projectDefaultTargetDir;
411       delete projectDefaultIntermediateObjDir;
412       delete compilerConfigsDir;
413       delete defaultCompiler;
414
415       delete ideFileDialogLocation;
416       delete ideProjectFileDialogLocation;
417       delete displayDriver;
418    }
419
420    void ForcePathSeparatorStyle(bool unixStyle)
421    {
422       char from, to;
423       if(unixStyle)
424          from = '\\', to = '/';
425       else
426          from = '/', to = '\\';
427       if(compilerConfigs && compilerConfigs.count)
428       {
429          int i;
430          for(config : compilerConfigs)
431          {
432             if(config.includeDirs && config.includeDirs.count)
433             {
434                for(i = 0; i < config.includeDirs.count; i++)
435                {
436                   if(config.includeDirs[i] && config.includeDirs[i][0])
437                      ChangeCh(config.includeDirs[i], from, to);
438                }
439             }
440             if(config.libraryDirs && config.libraryDirs.count)
441             {
442                for(i = 0; i < config.libraryDirs.count; i++)
443                {
444                   if(config.libraryDirs[i] && config.libraryDirs[i][0])
445                      ChangeCh(config.libraryDirs[i], from, to);
446                }
447             }
448             if(config.executableDirs && config.executableDirs.count)
449             {
450                for(i = 0; i < config.executableDirs.count; i++)
451                {
452                   if(config.executableDirs[i] && config.executableDirs[i][0])
453                      ChangeCh(config.executableDirs[i], from, to);
454                }
455             }
456          }
457       }
458       if(recentFiles && recentFiles.count)
459       {
460          int c;
461          for(c = 0; c < recentFiles.count; c++)
462          {
463             if(recentFiles[c] && recentFiles[c][0])
464                ChangeCh(recentFiles[c], from, to);
465          }
466       }
467       if(recentProjects && recentProjects.count)
468       {
469          int c;
470          for(c = 0; c < recentProjects.count; c++)
471          {
472             if(recentProjects[c] && recentProjects[c][0])
473                ChangeCh(recentProjects[c], from, to);
474          }
475       }
476       if(docDir && docDir[0])
477          ChangeCh(docDir, from, to);
478       if(ideFileDialogLocation && ideFileDialogLocation[0])
479          ChangeCh(ideFileDialogLocation, from, to);
480       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
481          ChangeCh(ideProjectFileDialogLocation, from, to);
482
483       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
484          ChangeCh(projectDefaultTargetDir, from, to);
485       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
486          ChangeCh(projectDefaultIntermediateObjDir, from, to);
487
488       if(compilerConfigsDir && compilerConfigsDir[0])
489          ChangeCh(compilerConfigsDir, from, to);
490    }
491
492    void ManagePortablePaths(char * location, bool makeAbsolute)
493    {
494       int c;
495       if(compilerConfigs && compilerConfigs.count)
496       {
497          for(config : compilerConfigs)
498          {
499             DirTypes t;
500             for(t = 0; t < DirTypes::enumSize; t++)
501             {
502                Array<String> dirs = null;
503                if(t == executables) dirs = config.executableDirs;
504                else if(t == includes) dirs = config.includeDirs;
505                else if(t == libraries) dirs = config.libraryDirs;
506                if(dirs && dirs.count)
507                {
508                   for(c = 0; c < dirs.count; c++)
509                   {
510                      if(dirs[c] && dirs[c][0])
511                         dirs[c] = UpdatePortablePath(dirs[c], location, makeAbsolute);
512                   }
513                }
514             }
515          }
516       }
517       if(recentFiles && recentFiles.count)
518       {
519          for(c = 0; c < recentFiles.count; c++)
520          {
521             if(recentFiles[c] && recentFiles[c][0])
522                recentFiles[c] = UpdatePortablePath(recentFiles[c], location, makeAbsolute);
523          }
524       }
525       if(recentProjects && recentProjects.count)
526       {
527          for(c = 0; c < recentProjects.count; c++)
528          {
529             if(recentProjects[c] && recentProjects[c][0])
530                recentProjects[c] = UpdatePortablePath(recentProjects[c], location, makeAbsolute);
531          }
532       }
533       if(docDir && docDir[0])
534          docDir = UpdatePortablePath(docDir, location, makeAbsolute);
535       if(ideFileDialogLocation && ideFileDialogLocation[0])
536          ideFileDialogLocation = UpdatePortablePath(ideFileDialogLocation, location, makeAbsolute);
537       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
538          ideProjectFileDialogLocation = UpdatePortablePath(ideProjectFileDialogLocation, location, makeAbsolute);
539
540       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
541          projectDefaultTargetDir = UpdatePortablePath(projectDefaultTargetDir, location, makeAbsolute);
542       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
543          projectDefaultIntermediateObjDir = UpdatePortablePath(projectDefaultIntermediateObjDir, location, makeAbsolute);
544
545       if(compilerConfigsDir && compilerConfigsDir[0])
546          compilerConfigsDir = UpdatePortablePath(compilerConfigsDir, location, makeAbsolute);
547    }
548
549    char * UpdatePortablePath(char * path, char * location, bool makeAbsolute)
550    {
551       char * output;
552       if(makeAbsolute)
553       {
554          char p[MAX_LOCATION];
555          strcpy(p, location);
556          PathCatSlash(p, path);
557          delete path;
558          output = CopyString(p);
559       }
560       else
561       {
562          PathRelationship rel = eString_PathRelated(path, location, null);
563          if(rel == subPath || rel == identical)
564          {
565             char p[MAX_LOCATION];
566             MakePathRelative(path, location, p);
567             if(!*p) strcpy(p, "./");
568             else ChangeCh(p, '\\', '/');
569             delete path;
570             output = CopyString(p);
571          }
572          else
573             output = path;
574       }
575       return output;
576    }
577
578    void AddRecentFile(char * fileName)
579    {
580       int c;
581       char * filePath = CopyString(fileName);
582       ChangeCh(filePath, '\\', '/');
583       for(c = 0; c<recentFiles.count; c++)
584       {
585          if(recentFiles[c] && !fstrcmp(recentFiles[c], filePath))
586          {
587             recentFiles.Delete((void *)&recentFiles[c]);
588             c--;
589          }
590       }
591       while(recentFiles.count >= MaxRecent)
592          recentFiles.Delete(recentFiles.GetLast());
593       recentFiles.Insert(null, filePath);
594       //UpdateRecentMenus(owner);
595    }
596
597    void AddRecentProject(char * projectName)
598    {
599       int c;
600       char * filePath = CopyString(projectName);
601       ChangeCh(filePath, '\\', '/');
602       for(c = 0; c<recentProjects.count; c++)
603       {
604          if(recentProjects[c] && !fstrcmp(recentProjects[c], filePath))
605          {
606             recentProjects.Delete((void *)&recentProjects[c]);
607             c--;
608          }
609       }
610       while(recentProjects.count >= MaxRecent)
611          recentProjects.Delete(recentProjects.GetLast());
612       recentProjects.Insert(null, filePath);
613       //UpdateRecentMenus(owner);
614    }
615 }
616
617 static const char * compilerTypeNames[CompilerType] = { "GCC", "TCC", "PCC", "VS8", "VS9", "VS10" };
618 static const char * compilerTypeVersionString[CompilerType] = { "", "", "", "8.00", "9.00", "10.00" };
619 static const char * compilerTypeSolutionFileVersionString[CompilerType] = { "", "", "", "9.00", "10.00", "11.00" };
620 static const char * compilerTypeYearString[CompilerType] = { "", "", "", "2005", "2008", "2010" };
621 static const char * compilerTypeProjectFileExtension[CompilerType] = { "", "", "", "vcproj", "vcproj", "vcxproj" };
622 // TODO: i18n with Array
623 static Array<String> compilerTypeLongNames
624 { [
625    $"GNU Compiler Collection (GCC) / GNU Make",
626    $"Tiny C Compiler / GNU Make",
627    $"Portable C Compiler / GNU Make",
628    $"Microsoft Visual Studio 2005 (8.0) Compiler",
629    $"Microsoft Visual Studio 2008 (9.0) Compiler",
630    $"Microsoft Visual Studio 2010 (10.0) Compiler"
631 ] };
632 const CompilerType firstCompilerType = gcc;
633 const CompilerType lastCompilerType = vs10;
634 public enum CompilerType
635 {
636    gcc, tcc, pcc, vs8, vs9, vs10;
637
638    property bool isVC
639    {
640       get { return this == vs8 || this == vs9 || this == vs10; }
641    }
642
643    property char *
644    {
645       get { return OnGetString(null, null, null); }
646       set
647       {  
648          if(value)
649          {
650             Platform c;
651             for(c = firstCompilerType; c <= lastCompilerType; c++)
652                if(!strcmpi(value, compilerTypeNames[c]))
653                   return c;
654          }
655          return gcc;
656       }
657    };
658
659    property char * longName { get { return OnGetString(null, (void*)1, null); } };
660    property char * versionString { get { return OnGetString(null, (void*)2, null); } };
661    property char * yearString { get { return OnGetString(null, (void*)3, null); } };
662    property char * projectFileExtension { get { return OnGetString(null, (void*)4, null); } };
663    property char * solutionFileVersionString { get { return OnGetString(null, (void*)5, null); } };
664
665    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
666    {
667       if(this >= firstCompilerType && this <= lastCompilerType)
668       {
669          if(tempString)
670             strcpy(tempString, compilerTypeNames[this]);
671          if(fieldData == null)
672             return compilerTypeNames[this];
673          else if(fieldData == (void*)1)
674             return compilerTypeLongNames[this];
675          else if(fieldData == (void*)2)
676             return compilerTypeVersionString[this];
677          else if(fieldData == (void*)3)
678             return compilerTypeYearString[this];
679          else if(fieldData == (void*)4)
680             return compilerTypeProjectFileExtension[this];
681          else if(fieldData == (void*)5)
682             return compilerTypeSolutionFileVersionString[this];
683       }
684       return null;
685    }
686 };
687
688 class CompilerConfig
689 {
690    class_no_expansion;
691
692    numJobs = 1;
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 * execPrefixCommand
751    {
752       set { delete execPrefixCommand; if(value && value[0]) execPrefixCommand = CopyString(value); }
753       get { return execPrefixCommand; }
754       isset { return execPrefixCommand && execPrefixCommand[0]; }
755    }
756    bool ccacheEnabled;
757    bool distccEnabled;
758    property char * distccHosts
759    {
760       set { delete distccHosts; if(value && value[0]) distccHosts = CopyString(value); }
761       get { return distccHosts; }
762       isset { return distccHosts && distccHosts[0]; }
763    }
764    property Array<String> includeDirs
765    {
766       set
767       {
768          includeDirs.Free();
769          if(value)
770          {
771             delete includeDirs;
772             includeDirs = value;
773          }
774       }
775       get { return includeDirs; }
776       isset { return includeDirs.count != 0; }
777    }
778    property Array<String> libraryDirs
779    {
780       set
781       {
782          libraryDirs.Free();
783          if(value)
784          {
785             delete libraryDirs;
786             libraryDirs = value;
787          }
788       }
789       get { return libraryDirs; }
790       isset { return libraryDirs.count != 0; }
791    }
792    property Array<String> executableDirs
793    {
794       set
795       {
796          executableDirs.Free();
797          if(value)
798          {
799             delete executableDirs;
800             executableDirs = value;
801          }
802       }
803       get { return executableDirs; }
804       isset { return executableDirs.count != 0; }
805    }
806    property Array<NamedString> environmentVars
807    {
808       set
809       {
810          environmentVars.Free();
811          if(value)
812          {
813             delete environmentVars;
814             environmentVars = value;
815          }
816       }
817       get { return environmentVars; }
818       isset { return environmentVars.count != 0; }
819    }
820 private:
821    Array<String> includeDirs { };
822    Array<String> libraryDirs { };
823    Array<String> executableDirs { };
824    // TODO: Can JSON parse and serialize maps?
825    //EnvironmentVariables { };
826    Array<NamedString> environmentVars { };
827    char * name;
828    char * makeCommand;
829    char * ecpCommand;
830    char * eccCommand;
831    char * ecsCommand;
832    char * earCommand;
833    char * cppCommand;
834    char * ccCommand;
835    char * execPrefixCommand;
836    char * distccHosts;
837    /*union
838    {
839       struct { Array<String> includes, libraries, executables; };
840       Array<String> dirs[DirTypes];
841    }*/
842
843    ~CompilerConfig()
844    {
845       delete name;
846       delete ecpCommand;
847       delete eccCommand;
848       delete ecsCommand;
849       delete earCommand;
850       delete cppCommand;
851       delete ccCommand;
852       delete makeCommand;
853       delete execPrefixCommand;
854       delete distccHosts;
855       if(environmentVars) environmentVars.Free();
856       if(includeDirs) { includeDirs.Free(); }
857       if(libraryDirs) { libraryDirs.Free(); }
858       if(executableDirs) { executableDirs.Free(); }
859    }
860    CompilerConfig Copy()
861    {
862       CompilerConfig copy
863       {
864          name,
865          readOnly,
866          type,
867          targetPlatform,
868          numJobs,
869          makeCommand,
870          ecpCommand,
871          eccCommand,
872          ecsCommand,
873          earCommand,
874          cppCommand,
875          ccCommand,
876          execPrefixCommand,
877          ccacheEnabled,
878          distccEnabled,
879          distccHosts
880       };
881       for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
882       for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
883       for(s : executableDirs) copy.executableDirs.Add(CopyString(s));
884       for(ns : environmentVars) copy.environmentVars.Add(NamedString { name = ns.name, string = ns.string });
885
886       incref copy;
887       return copy;
888    }
889 }