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