ide/settings: Fixed write locking on Unix
[sdk] / ide / src / IDESettings.ec
index d5afbaf..8a19e0d 100644 (file)
@@ -25,6 +25,8 @@ import "ide"
 import "process"
 #endif
 
+IDEConfigHolder ideConfig { };
+
 IDESettings ideSettings;
 
 IDESettingsContainer settingsContainer
@@ -243,6 +245,7 @@ CompilerConfig MakeDefaultCompiler(const char * name, bool readOnly)
    return defaultCompiler;
 }
 
+//#define SETTINGS_TEST
 #ifdef SETTINGS_TEST
 define settingsDir = ".ecereIDE-SettingsTest";
 define ideSettingsName = "ecereIDE-SettingsTest";
@@ -251,6 +254,47 @@ define settingsDir = ".ecereIDE";
 define ideSettingsName = "ecereIDE";
 #endif
 
+class IDEConfigHolder
+{
+   CompilerConfigs compilers { };
+   RecentFiles recentFiles { };
+   RecentWorkspaces recentWorkspaces { };
+
+   property CompilerConfigs compilers
+   {
+      set { compilers.Free(); delete compilers; compilers = value; }
+      get { return compilers; }
+   }
+   property RecentFiles recentFiles
+   {
+      set { recentFiles.Free(); delete recentFiles; recentFiles = value; }
+      get { return recentFiles; }
+   }
+   property RecentWorkspaces recentProjects
+   {
+      set { recentWorkspaces.Free(); delete recentWorkspaces; recentWorkspaces = value; }
+      get { return recentWorkspaces; }
+   }
+
+   ~IDEConfigHolder()
+   {
+      compilers.Free();
+      recentFiles.Free();
+      recentWorkspaces.Free();
+   }
+
+   void forcePathSeparatorStyle(bool unixStyle)
+   {
+      char from, to;
+      if(unixStyle)
+         from = '\\', to = '/';
+      else
+         from = '/', to = '\\';
+      recentFiles.changeChar(from, to);
+      recentWorkspaces.changeChar(from, to);
+   }
+}
+
 class IDESettingsContainer : GlobalSettings
 {
    property bool useNewConfigurationFiles
@@ -276,6 +320,50 @@ class IDESettingsContainer : GlobalSettings
 
    char moduleLocation[MAX_LOCATION];
 
+   FileMonitor recentFilesMonitor
+   {
+      this, fileChange = { modified = true };
+
+      bool OnFileNotify(FileChange action, const char * param)
+      {
+         File f;
+         recentFilesMonitor.StopMonitoring();
+         f = FileOpen(recentFilesMonitor.fileName, read);
+         if(f)
+         {
+            int c;
+            bool locked;
+            for(c = 0; c < 10 && !(locked = f.Lock(shared, 0, 0, false)); c++) ecere::sys::Sleep(0.01);
+            RecentFiles::read();
+            f.Unlock(0,0,true);
+            delete f;
+         }
+         return true;
+      }
+   };
+
+   FileMonitor recentProjectsMonitor
+   {
+      this, fileChange = { modified = true };
+
+      bool OnFileNotify(FileChange action, const char * param)
+      {
+         File f;
+         recentProjectsMonitor.StopMonitoring();
+         f = FileOpen(recentProjectsMonitor.fileName, read);
+         if(f)
+         {
+            int c;
+            bool locked;
+            for(c = 0; c < 10 && !(locked = f.Lock(shared, 0, 0, false)); c++) ecere::sys::Sleep(0.01);
+            RecentWorkspaces::read();
+            f.Unlock(0,0,true);
+            delete f;
+         }
+         return true;
+      }
+   };
+
 private:
    bool oldConfig;
    FileSize settingsFileSize;
@@ -419,26 +507,29 @@ private:
 
       CloseAndMonitor();
       FileGetSize(settingsFilePath, &settingsFileSize);
-      CompilerConfigs::fix();
       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
          data.ManagePortablePaths(moduleLocation, true);
       data.ForcePathSeparatorStyle(true);
 
-#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
-      globalSettingsDialog.ideSettings = data;
+      // Import from previous ecereIDE settings
       if(oldConfig)
       {
-         ide.updateRecentMenus();
-         ide.UpdateCompilerConfigs(true);
-      }
-#endif
+         data.compilerConfigs.ensureDefaults();
+         data.compilerConfigs.write(null);
+         data.compilerConfigs.Free();
+
+         data.recentFiles.write();
+         data.recentFiles.Free();
+
+         data.recentProjects.write();
+         data.recentProjects.Free();
 
-      if(oldConfig)
-      {
-         useNewConfigurationFiles = true;
          Save();
-         CompilerConfigs::write(null);
       }
+
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+      globalSettingsDialog.ideSettings = data;
+#endif
       return result;
    }
 
@@ -480,10 +571,7 @@ static Map<String, String> getCompilerConfigFilePathsByName(const char * path)
          char * path = CopyString(fl.path);
          MakeSlashPath(path);
          GetLastDirectory(path, name);
-         {
-            char * s = strstr(name, ".");
-            if(s) *s = 0;
-         }
+         StripExtension(name);
          map[name] = path;
       }
    }
@@ -502,10 +590,7 @@ static Map<String, CompilerConfig> getCompilerConfigsByName(const char * path)
          char * path = CopyString(fl.path);
          MakeSlashPath(path);
          GetLastDirectory(path, name);
-         {
-            char * s = strstr(name, ".");
-            if(s) *s = 0;
-         }
+         StripExtension(name);
          {
             CompilerConfig ccfg = CompilerConfig::read(path);
             if(ccfg)
@@ -532,7 +617,7 @@ static void getConfigFilePath(char * path, Class _class, char * dir, const char
       if(configName)
       {
          PathCatSlash(path, configName);
-         ChangeExtension(path, "econ", path);
+         strcat(path, ".econ");
       }
    }
    else if(_class == class(RecentFilesData))
@@ -544,11 +629,11 @@ static void getConfigFilePath(char * path, Class _class, char * dir, const char
 static SettingsIOResult writeConfigFile(const char * path, Class dataType, void * data)
 {
    SettingsIOResult result = error;
-   SafeFile sf;
-   sf = safeWriteFileOpen(path, write);
-   if(sf.file)
+   SafeFile sf = SafeFile::open(path, write);
+   if(sf)
    {
       WriteECONObject(sf.file, dataType, data, 0);
+      sf.sync();
       delete sf;
       result = success;
    }
@@ -563,7 +648,7 @@ static SettingsIOResult readConfigFile(const char * path, Class dataType, void *
    SafeFile sf;
    if(!FileExists(path))
       result = fileNotFound;
-   else if((sf = safeWriteFileOpen(path, read)))
+   else if((sf = SafeFile::open(path, read)))
    {
       JSONResult jsonResult;
       {
@@ -589,81 +674,117 @@ static SettingsIOResult readConfigFile(const char * path, Class dataType, void *
 class SafeFile
 {
    File file;
-   File lock;
    FileOpenMode mode;
+   char path[MAX_LOCATION];
    char tmp[MAX_LOCATION];
-   char lck[MAX_LOCATION];
 
-   ~SafeFile()
+   SafeFile ::open(const char * path, FileOpenMode mode)
    {
-      delete file;
-      if(mode == write)
-         DeleteFile(tmp);
-      if(lock)
+      SafeFile result = null;
+      if(mode == write || mode == read)
       {
-         delete lock;
-         DeleteFile(lck);
-      }
-   }
-}
+         SafeFile sf { mode = mode };
+         int c;
+         bool locked = false;
+         FileLock lockType = mode == write ? exclusive : shared;
 
-SafeFile safeWriteFileOpen(const char * path, FileOpenMode mode)
-{
-   SafeFile sf { mode = mode };
-   strcpy(sf.lck, path);
-   strcat(sf.lck, ".lck");
-   strcpy(sf.tmp, path);
-   strcat(sf.tmp, ".tmp");
-   if(mode == write)
-   {
-      sf.lock = FileOpen(sf.lck, write);
-      if(sf.lock && sf.lock.Lock(exclusive, 0, 0, false))
-      {
-         if(sf.tmp && FileExists(path).isFile)
-            MoveFile(path, sf.tmp);
-         sf.file = FileOpen(path, write);
+         strcpy(sf.path, path);
+         strcpy(sf.tmp, path);
+         strcat(sf.tmp, ".tmp");
+         if(mode == write && FileExists(sf.tmp).isFile)
+            DeleteFile(sf.tmp);
+
+         if(mode == write)
+         {
+            sf.file = FileOpen(sf.tmp, readWrite);
+            if(!sf.file)
+            {
+               sf.file = FileOpen(sf.tmp, writeRead);
+               if(sf.file)
+               {
+                  delete sf.file;
+                  sf.file = FileOpen(sf.tmp, readWrite);
+               }
+            }
+         }
+         else
+            sf.file = FileOpen(path, mode);
+         if(sf.file)
+         {
+            for(c = 0; c < 10 && !(locked = sf.file.Lock(lockType, 0, 0, false)); c++) Sleep(0.01);
+            if(locked)
+               result = sf;
+            else if(mode == write)
+               PrintLn($"warning: SafeFile::open: unable to obtain exclusive lock on temporary file for writing: ", sf.tmp);
+            else
+               PrintLn($"warning: SafeFile::open: unable to obtain shared lock on file for reading: ", path);
+         }
+         else if(mode == write)
+            PrintLn($"warning: SafeFile::open: unable to open temporary file for writing: ", sf.tmp);
+         else
+            PrintLn($"warning: SafeFile::open: unable to open file for reading: ", path);
+
+         if(!result)
+            delete sf;
       }
       else
-         PrintLn($"warning: safeWriteFileOpen: unable to obtain exclusive lock for writing: ", sf.lck);
+         PrintLn($"warning: SafeFile::open: does not yet support FileOpenMode::", mode);
+      return result;
    }
-   else if(mode == read)
+
+   void sync()
    {
-      int c;
-      bool locked = false;
-      bool failed = false;
-      for(c = 0; c < 10 && !(failed = sf.tmp && FileExists(sf.tmp).isFile); c++) Sleep(0.01);
-      if(failed)
+      if(file && mode == write)
       {
-         sf.lock = FileOpen(sf.lck, write);
-         if(sf.lock && sf.lock.Lock(exclusive, 0, 0, false))
+         int c;
+         File f = FileOpen(path, readWrite);
+         if(!f)
          {
-            if(FileExists(sf.tmp).isFile)
+            f = FileOpen(path, writeRead);
+            if(f)
             {
-               if(FileExists(path).isFile)
-                  DeleteFile(path);
-               MoveFile(sf.tmp, path);
+               delete f;
+               f = FileOpen(path, readWrite);
+            }
+         }
+         if(f)
+         {
+            bool locked = true;
+            for(c = 0; c < 10 && !(locked = f.Lock(exclusive, 0,0, false)); c++) Sleep(0.01);
+
+            if(locked)
+            {
+               f.Unlock(0,0, false);
+               delete f;
+               file.Unlock(0,0, false);
+               delete file;
+
+               for(c = 0; c < 10; c++)
+               {
+                  if(MoveFileEx(tmp, path, { true, true }))
+                     break;
+                  else
+                     Sleep(0.01);
+               }
             }
             else
-               PrintLn($"warning: safeWriteFileOpen: file is gone: ", sf.tmp);
-            delete sf.lock;
-            DeleteFile(sf.lck);
+            {
+               delete f;
+               PrintLn($"warning: SafeFile::sync: failed to lock file", mode);
+            }
          }
-         else
-            PrintLn($"warning: safeWriteFileOpen: unable to obtain exclusive lock for failed write repair: ", sf.lck);
       }
-      sf.lock = FileOpen(sf.lck, write);
-      if(sf.lock) delete sf.lock;
-      sf.lock = FileOpen(sf.lck, read);
-      if(sf.lock)
+   }
+
+
+   ~SafeFile()
+   {
+      if(file)
       {
-         for(c = 0; c < 10 && !(locked = sf.lock.Lock(shared, 0, 0, false)); c++) Sleep(0.01);
-         if(locked)
-            sf.file = FileOpen(path, read);
+         file.Unlock(0,0, false);
+         delete file;
       }
    }
-   else
-      PrintLn($"warning: safeWriteFileOpen: does not yet support FileOpenMode::", mode);
-   return sf;
 }
 
 class RecentFilesData
@@ -683,7 +804,7 @@ class IDESettings : GlobalSettingsData
 public:
    property CompilerConfigs compilerConfigs
    {
-      set { if(compilerConfigs) compilerConfigs.Free(); delete compilerConfigs; if(value) compilerConfigs = value; }
+      set { if(settingsContainer.oldConfig) { if(compilerConfigs) compilerConfigs.Free(); delete compilerConfigs; if(value) compilerConfigs = value; } }
       get { return compilerConfigs; }
       isset { return false; }
    }
@@ -776,25 +897,6 @@ private:
    RecentFiles recentFiles { };
    RecentWorkspaces recentProjects { };
 
-   CompilerConfig GetCompilerConfig(const String compilerName)
-   {
-      const char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
-      CompilerConfig compilerConfig = null;
-      for(compiler : compilerConfigs)
-      {
-         if(!strcmp(compiler.name, name))
-         {
-            compilerConfig = compiler;
-            break;
-         }
-      }
-      if(!compilerConfig && compilerConfigs.count)
-         compilerConfig = compilerConfigs.firstIterator.data;
-      if(compilerConfig)
-         incref compilerConfig;
-      return compilerConfig;
-   }
-
    ~IDESettings()
    {
       compilerConfigs.Free();
@@ -973,24 +1075,24 @@ class RecentFiles : RecentPaths
       readConfigFile(path, _class, &d);
       if(d && d.recentFiles && d.recentFiles.count)
       {
-         IDESettings s = (IDESettings)settingsContainer.data;
-         s.property::recentFiles = d.recentFiles;
+         ideConfig.recentFiles = d.recentFiles;
          d.recentFiles = null;
 #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
          ide.updateRecentFilesMenu();
 #endif
       }
       delete d;
+      settingsContainer.recentFilesMonitor.fileName = path;
+      settingsContainer.recentFilesMonitor.StartMonitoring();
    }
 
-   void ::write()
+   void write()
    {
       char path[MAX_LOCATION];
-      IDESettings s = (IDESettings)settingsContainer.data;
       RecentFilesData d { };
       Class _class = class(RecentFilesData);
       getConfigFilePath(path, _class, null, null);
-      d.recentFiles = s.recentFiles;
+      d.recentFiles = this;
       writeConfigFile(path, _class, d);
       d.recentFiles = null;
       delete d;
@@ -1013,24 +1115,24 @@ class RecentWorkspaces : RecentPaths
       readConfigFile(path, _class, &d);
       if(d && d.recentWorkspaces && d.recentWorkspaces.count)
       {
-         IDESettings s = (IDESettings)settingsContainer.data;
-         s.property::recentProjects = d.recentWorkspaces;
+         ideConfig.recentProjects = d.recentWorkspaces;
          d.recentWorkspaces = null;
 #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
          ide.updateRecentProjectsMenu();
 #endif
       }
+      settingsContainer.recentProjectsMonitor.fileName = path;
+      settingsContainer.recentProjectsMonitor.StartMonitoring();
       delete d;
    }
 
-   void ::write()
+   void write()
    {
       char path[MAX_LOCATION];
-      IDESettings s = (IDESettings)settingsContainer.data;
       RecentWorkspacesData d { };
       Class _class = class(RecentWorkspacesData);
       getConfigFilePath(path, _class, null, null);
-      d.recentWorkspaces = s.recentProjects;
+      d.recentWorkspaces = this;
       writeConfigFile(path, _class, d);
       d.recentWorkspaces = null;
       delete d;
@@ -1057,10 +1159,10 @@ class RecentPaths : Array<String>
       return Array::Add((T)filePath);
    }
 
-   IteratorPointer addRecent(T value)
+   IteratorPointer addRecent(const String value)
    {
       int c;
-      char * filePath = (char *)value;
+      char * filePath = CopyString((char *)value);
       IteratorPointer ip;
       ChangeCh(filePath, '\\', '/');
       for(c = 0; c < count; c++)
@@ -1244,11 +1346,23 @@ public:
       get { return objectFileExt && objectFileExt[0] ? objectFileExt : objectDefaultFileExt ; }
       isset { return objectFileExt && objectFileExt[0] && strcmp(objectFileExt, objectDefaultFileExt); }
    }
-   property const char * outputFileExt
+   property const char * staticLibFileExt
    {
-      set { delete outputFileExt; if(value && value[0]) outputFileExt = CopyString(value); }
-      get { return outputFileExt; }
-      isset { return outputFileExt && outputFileExt[0]; }
+      set { delete staticLibFileExt; if(value && value[0]) staticLibFileExt = CopyString(value); }
+      get { return staticLibFileExt; }
+      isset { return staticLibFileExt && staticLibFileExt[0]; }
+   }
+   property const char * sharedLibFileExt
+   {
+      set { delete sharedLibFileExt; if(value && value[0]) sharedLibFileExt = CopyString(value); }
+      get { return sharedLibFileExt; }
+      isset { return sharedLibFileExt && sharedLibFileExt[0]; }
+   }
+   property const char * executableFileExt
+   {
+      set { delete executableFileExt; if(value && value[0]) executableFileExt = CopyString(value); }
+      get { return executableFileExt; }
+      isset { return executableFileExt && executableFileExt[0]; }
    }
    property const char * executableLauncher
    {
@@ -1281,6 +1395,7 @@ public:
       isset { return sysroot && sysroot[0]; }
    }
    bool resourcesDotEar;
+   bool noStripTarget;
    property Array<String> includeDirs
    {
       set
@@ -1393,6 +1508,20 @@ public:
       get { return compilerFlags; }
       isset { return compilerFlags.count != 0; }
    }
+   property Array<String> cxxFlags
+   {
+      set
+      {
+         cxxFlags.Free();
+         if(value)
+         {
+            delete cxxFlags;
+            cxxFlags = value;
+         }
+      }
+      get { return cxxFlags; }
+      isset { return cxxFlags.count != 0; }
+   }
    property Array<String> linkerFlags
    {
       set
@@ -1420,6 +1549,23 @@ public:
       get { return executableLauncher; }
       isset { return false; }
    }
+   property const char * outputFileExt
+   {
+      set { delete executableFileExt; if(value && value[0]) executableFileExt = CopyString(value); }
+      get { return executableFileExt; }
+      isset { return false; }
+   }
+   // utility
+   property bool hasDocumentOutput
+   {
+      get
+      {
+         bool result = executableFileExt && executableFileExt[0] &&
+               (!strcmpi(executableFileExt, "htm") || !strcmpi(executableFileExt, "html"));
+         return result;
+      }
+      isset { return false; }
+   }
 private:
    Array<String> includeDirs { };
    Array<String> libraryDirs { };
@@ -1431,6 +1577,7 @@ private:
    Array<String> excludeLibs { };
    Array<String> eCcompilerFlags { };
    Array<String> compilerFlags { };
+   Array<String> cxxFlags { };
    Array<String> linkerFlags { };
    char * name;
    char * makeCommand;
@@ -1444,7 +1591,9 @@ private:
    char * ldCommand;
    char * arCommand;
    char * objectFileExt;
-   char * outputFileExt;
+   char * staticLibFileExt;
+   char * sharedLibFileExt;
+   char * executableFileExt;
    char * executableLauncher;
    char * distccHosts;
    char * gnuToolchainPrefix;
@@ -1468,7 +1617,9 @@ private:
       delete ldCommand;
       delete arCommand;
       delete objectFileExt;
-      delete outputFileExt;
+      delete staticLibFileExt;
+      delete sharedLibFileExt;
+      delete executableFileExt;
       delete makeCommand;
       delete executableLauncher;
       delete distccHosts;
@@ -1481,6 +1632,7 @@ private:
       if(prepDirectives) { prepDirectives.Free(); }
       if(excludeLibs) { excludeLibs.Free(); }
       if(compilerFlags) { compilerFlags.Free(); }
+      if(cxxFlags) { cxxFlags.Free(); }
       if(eCcompilerFlags) { eCcompilerFlags.Free(); }
       if(linkerFlags) { linkerFlags.Free(); }
    }
@@ -1488,7 +1640,13 @@ private:
    int OnCompare(CompilerConfig b)
    {
       int result;
-      if(!(result = name.OnCompare(b.name)) &&
+      if(
+         !(result = type.OnCompare(b.type)) &&
+         !(result = targetPlatform.OnCompare(b.targetPlatform)) &&
+         !(result = numJobs.OnCompare(b.numJobs)));
+
+      if(!result &&
+         !(result = name.OnCompare(b.name)) &&
          !(result = ecpCommand.OnCompare(b.ecpCommand)) &&
          !(result = eccCommand.OnCompare(b.eccCommand)) &&
          !(result = ecsCommand.OnCompare(b.ecsCommand)) &&
@@ -1504,8 +1662,7 @@ private:
          !(result = executableLauncher.OnCompare(b.executableLauncher)) &&
          !(result = distccHosts.OnCompare(b.distccHosts)) &&
          !(result = gnuToolchainPrefix.OnCompare(b.gnuToolchainPrefix)) &&
-         !(result = sysroot.OnCompare(b.sysroot)))
-         ;
+         !(result = sysroot.OnCompare(b.sysroot)));
 
       if(!result &&
          !(result = includeDirs.OnCompare(b.includeDirs)) &&
@@ -1514,11 +1671,10 @@ private:
          !(result = environmentVars.OnCompare(b.environmentVars)) &&
          !(result = prepDirectives.OnCompare(b.prepDirectives)) &&
          !(result = excludeLibs.OnCompare(b.excludeLibs)) &&
+         !(result = cxxFlags.OnCompare(b.cxxFlags)) &&
          !(result = eCcompilerFlags.OnCompare(b.eCcompilerFlags)) &&
          !(result = compilerFlags.OnCompare(b.compilerFlags)) &&
-         !(result = linkerFlags.OnCompare(b.linkerFlags)))
-         ;
-
+         !(result = linkerFlags.OnCompare(b.linkerFlags)));
       return result;
    }
 
@@ -1543,7 +1699,9 @@ public:
          arCommand,
          ldCommand,
          objectFileExt,
-         outputFileExt,
+         staticLibFileExt,
+         sharedLibFileExt,
+         executableFileExt,
          executableLauncher,
          ccacheEnabled,
          distccEnabled,
@@ -1551,7 +1709,8 @@ public:
          distccHosts,
          gnuToolchainPrefix,
          sysroot,
-         resourcesDotEar
+         resourcesDotEar,
+         noStripTarget
       };
       for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
       for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
@@ -1560,6 +1719,7 @@ public:
       for(s : prepDirectives) copy.prepDirectives.Add(CopyString(s));
       for(s : excludeLibs) copy.excludeLibs.Add(CopyString(s));
       for(s : compilerFlags) copy.compilerFlags.Add(CopyString(s));
+      for(s : cxxFlags) copy.cxxFlags.Add(CopyString(s));
       for(s : eCcompilerFlags) copy.eCcompilerFlags.Add(CopyString(s));
       for(s : linkerFlags) copy.linkerFlags.Add(CopyString(s));
 
@@ -1592,48 +1752,70 @@ public:
 
 class CompilerConfigs : List<CompilerConfig>
 {
-   void ::fix()
+   CompilerConfig GetCompilerConfig(const String compilerName)
+   {
+      const char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
+      CompilerConfig compilerConfig = null;
+      for(compiler : this)
+      {
+         if(!strcmp(compiler.name, name))
+         {
+            compilerConfig = compiler;
+            break;
+         }
+      }
+      if(!compilerConfig && count)
+         compilerConfig = this[0];
+      if(compilerConfig)
+      {
+         incref compilerConfig;
+         if(compilerConfig._refCount == 1)
+            incref compilerConfig;
+      }
+      return compilerConfig;
+   }
+
+   void ensureDefaults()
    {
-      IDESettings s = (IDESettings)settingsContainer.data;
       // Ensure we have a default compiler
-      CompilerConfig defaultCompiler = null;
-      defaultCompiler = s.GetCompilerConfig(defaultCompilerName);
+      CompilerConfig defaultCompiler = GetCompilerConfig(defaultCompilerName);
       if(!defaultCompiler)
       {
          defaultCompiler = MakeDefaultCompiler(defaultCompilerName, true);
-         s.compilerConfigs.Insert(null, defaultCompiler);
+         Insert(null, defaultCompiler);
          defaultCompiler = null;
       }
       delete defaultCompiler;
 
-      if(s.compilerConfigs)
+      for(ccfg : this)
       {
-         for(ccfg : s.compilerConfigs)
-         {
-            if(!ccfg.ecpCommand || !ccfg.ecpCommand[0])
-               ccfg.ecpCommand = ecpDefaultCommand;
-            if(!ccfg.eccCommand || !ccfg.eccCommand[0])
-               ccfg.eccCommand = eccDefaultCommand;
-            if(!ccfg.ecsCommand || !ccfg.ecsCommand[0])
-               ccfg.ecsCommand = ecsDefaultCommand;
-            if(!ccfg.earCommand || !ccfg.earCommand[0])
-               ccfg.earCommand = earDefaultCommand;
-            if(!ccfg.cppCommand || !ccfg.cppCommand[0])
-               ccfg.cppCommand = cppDefaultCommand;
-            if(!ccfg.ccCommand || !ccfg.ccCommand[0])
-               ccfg.ccCommand = ccDefaultCommand;
-            if(!ccfg.cxxCommand || !ccfg.cxxCommand[0])
-               ccfg.cxxCommand = cxxDefaultCommand;
-            /*if(!ccfg.ldCommand || !ccfg.ldCommand[0])
-               ccfg.ldCommand = ldDefaultCommand;*/
-            if(!ccfg.arCommand || !ccfg.arCommand[0])
-               ccfg.arCommand = arDefaultCommand;
-            if(!ccfg.objectFileExt || !ccfg.objectFileExt[0])
-               ccfg.objectFileExt = objectDefaultFileExt;
-            /*if(!ccfg.outputFileExt || !ccfg.outputFileExt[0])
-               ccfg.outputFileExt = outputDefaultFileExt;*/
-            if(!ccfg._refCount) incref ccfg;
-         }
+         if(!ccfg.ecpCommand || !ccfg.ecpCommand[0])
+            ccfg.ecpCommand = ecpDefaultCommand;
+         if(!ccfg.eccCommand || !ccfg.eccCommand[0])
+            ccfg.eccCommand = eccDefaultCommand;
+         if(!ccfg.ecsCommand || !ccfg.ecsCommand[0])
+            ccfg.ecsCommand = ecsDefaultCommand;
+         if(!ccfg.earCommand || !ccfg.earCommand[0])
+            ccfg.earCommand = earDefaultCommand;
+         if(!ccfg.cppCommand || !ccfg.cppCommand[0])
+            ccfg.cppCommand = cppDefaultCommand;
+         if(!ccfg.ccCommand || !ccfg.ccCommand[0])
+            ccfg.ccCommand = ccDefaultCommand;
+         if(!ccfg.cxxCommand || !ccfg.cxxCommand[0])
+            ccfg.cxxCommand = cxxDefaultCommand;
+         /*if(!ccfg.ldCommand || !ccfg.ldCommand[0])
+            ccfg.ldCommand = ldDefaultCommand;*/
+         if(!ccfg.arCommand || !ccfg.arCommand[0])
+            ccfg.arCommand = arDefaultCommand;
+         if(!ccfg.objectFileExt || !ccfg.objectFileExt[0])
+            ccfg.objectFileExt = objectDefaultFileExt;
+         /*if(!ccfg.staticLibFileExt || !ccfg.staticLibFileExt[0])
+            ccfg.staticLibFileExt = staticLibDefaultFileExt;*/
+         /*if(!ccfg.sharedLibFileExt || !ccfg.sharedLibFileExt[0])
+            ccfg.sharedLibFileExt = sharedLibDefaultFileExt;*/
+         /*if(!ccfg.executableFileExt || !ccfg.executableFileExt[0])
+            ccfg.executableFileExt = outputDefaultFileExt;*/
+         if(!ccfg._refCount) incref ccfg;
       }
    }
 
@@ -1642,20 +1824,21 @@ class CompilerConfigs : List<CompilerConfig>
       AVLTree<String> list { };
       for(ccfg : this)
       {
-         for(occfg : oldConfigs)
+         bool found = false;
+         for(occfg : oldConfigs; !strcmp(ccfg.name, occfg.name))
          {
-            if(!strcmp(ccfg.name, occfg.name))
-            {
-               if(ccfg.OnCompare(occfg))
-                  list.Add(CopyString(ccfg.name));
-               break;
-            }
+            found = true;
+            if(ccfg.OnCompare(occfg))
+               list.Add(CopyString(ccfg.name));
+            break;
          }
+         if(!found)
+            list.Add(CopyString(ccfg.name));
       }
       return list;
    }
 
-   void ::read()
+   void read()
    {
       if(settingsContainer.settingsFilePath)
       {
@@ -1665,33 +1848,32 @@ class CompilerConfigs : List<CompilerConfig>
          getConfigFilePath(path, _class, dir, null);
          if(dir[0])
          {
-            CompilerConfigs ccfgs { };
             AVLTree<const String> addedConfigs { };
-            IDESettings s = (IDESettings)settingsContainer.data;
             Map<String, CompilerConfig> compilerConfigsByName = getCompilerConfigsByName(dir);
             MapIterator<const String, CompilerConfig> it { map = compilerConfigsByName };
             if(it.Index("Default", false))
             {
                CompilerConfig ccfg = it.data;
-               ccfgs.Add(ccfg.Copy());
+               Add(ccfg.Copy());
                addedConfigs.Add(ccfg.name);
             }
             for(ccfg : compilerConfigsByName)
             {
                if(!addedConfigs.Find(ccfg.name))
                {
-                  ccfgs.Add(ccfg.Copy());
+                  Add(ccfg.Copy());
                   addedConfigs.Add(ccfg.name);
                }
             }
-            for(ccfg : s.compilerConfigs)
+            /*
+            for(ccfg : this)
             {
                if(!addedConfigs.Find(ccfg.name))
-                  ccfgs.Add(ccfg.Copy());
+                  Add(ccfg.Copy());
             }
+            */
             delete addedConfigs;
-            s.property::compilerConfigs = ccfgs;
-            fix();
+            ensureDefaults();
             compilerConfigsByName.Free();
             delete compilerConfigsByName;
 #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
@@ -1701,17 +1883,16 @@ class CompilerConfigs : List<CompilerConfig>
       }
    }
 
-   void ::write(AVLTree<String> cfgsToWrite)
+   void write(AVLTree<String> cfgsToWrite)
    {
       char dir[MAX_LOCATION];
       char path[MAX_LOCATION];
       Map<String, String> paths;
-      IDESettings s = (IDESettings)settingsContainer.data;
       getConfigFilePath(path, class(CompilerConfig), dir, null);
       paths = getCompilerConfigFilePathsByName(dir);
       {
          MapIterator<String, String> it { map = paths };
-         for(c : s.compilerConfigs)
+         for(c : this)
          {
             CompilerConfig ccfg = c;
             if(!cfgsToWrite || cfgsToWrite.Find(ccfg.name))