epj2make, ide: makefile generation, global settings dialog: add CXXFLAGS to compiler...
[sdk] / ide / src / project / ProjectNode.ec
index ebfadde..d6c478d 100644 (file)
@@ -1,4 +1,4 @@
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
 import "ide"
 #else
 #ifdef ECERE_STATIC
@@ -12,7 +12,18 @@ import "Project"
 static define app = ((GuiApplication)__thisModule);
 #endif
 
-bool eString_PathInsideOfMore(char * path, char * of, char * pathRest)
+#define OPTION(x) ((uint)(uintptr)(&((ProjectOptions)0).x))
+
+static void OutputLog(const char * string)
+{
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+   ide.outputView.buildBox.Log(string);
+#else
+   printf("%s", string);
+#endif
+}
+
+bool eString_PathInsideOfMore(const char * path, const char * of, char * pathRest)
 {
    if(!path[0] || !of[0])
       return false;  // What to do here? Ever used?
@@ -24,7 +35,7 @@ bool eString_PathInsideOfMore(char * path, char * of, char * pathRest)
       strcpy(pathRest, path);
       for(; ofRest[0] && pathRest[0];)
       {
-         SplitDirectory(ofRest, ofPart, ofRest);      
+         SplitDirectory(ofRest, ofPart, ofRest);
          SplitDirectory(pathRest, pathPart, pathRest);
          if(fstrcmp(pathPart, ofPart))
             return false;
@@ -41,10 +52,10 @@ enum NodeTypes { project, file, folder, resources, folderOpen };
 enum NodeIcons
 {
    genFile, ewsFile, epjFile, folder, openFolder, ecFile, ehFile,
-   cFile, hFile, cppFile, hppFile, textFile, webFile, pictureFile, soundFile,
-   archiveFile, packageFile, opticalMediaImageFile, mFile;
+   sFile, cFile, hFile, cppFile, hppFile, textFile, webFile, pictureFile, soundFile,
+   archiveFile, packageFile, opticalMediaImageFile, mFile, mmFile;
 
-   NodeIcons ::SelectFileIcon(char * filePath)
+   NodeIcons ::SelectFileIcon(const char * filePath)
    {
       NodeIcons icon;
       if(filePath && filePath[0])
@@ -67,12 +78,16 @@ enum NodeIcons
             else if(!strcmpi(extension, "hpp") || !strcmpi(extension, "hh") ||
                   !strcmpi(extension, "hxx"))
                icon = hppFile;
+            else if(!strcmpi(extension, "s"))
+               icon = sFile;
             else if(!strcmpi(extension, "c"))
                icon = cFile;
             else if(!strcmpi(extension, "h"))
                icon = hFile;
             else if(!strcmpi(extension, "m"))
                icon = mFile;
+            else if(!strcmpi(extension, "mm"))
+               icon = mmFile;
             else if(!strcmpi(extension, "txt") || !strcmpi(extension, "text") ||
                   !strcmpi(extension, "nfo") || !strcmpi(extension, "info"))
                icon = textFile;
@@ -162,6 +177,57 @@ class TwoStrings : struct
    }
 }
 
+class DotMain : bool
+{
+   //property char * { set { } }
+   DotMain ::FromFileName(const char * fileName)
+   {
+      DotMain dotMain = false;
+      if(fileName && fileName[0])
+      {
+         char ext[MAX_EXTENSION];
+         GetExtension(fileName, ext);
+         if(!strcmp(ext, "c") || !strcmp(ext, "ec"))
+         {
+            char stripExt[MAX_LOCATION];
+            strcpy(stripExt, fileName);
+            StripExtension(stripExt);
+            GetExtension(stripExt, ext);
+            if(!strcmp(ext, "main"))
+               dotMain = true;
+         }
+      }
+      return dotMain;
+   }
+}
+
+enum IntermediateFileType
+{
+   none, ec, c, sym, imp, bowl, o;
+
+   //property char * { set { } }
+   IntermediateFileType ::FromExtension(char * extension)
+   {
+      IntermediateFileType type = none;
+      if(extension && extension[0])
+      {
+         if(!fstrcmp(extension, "ec"))
+            type = ec;
+         else if(!fstrcmp(extension, "c"))
+            type = c;
+         else if(!fstrcmp(extension, "sym"))
+            type = sym;
+         else if(!fstrcmp(extension, "imp"))
+            type = imp;
+         else if(!fstrcmp(extension, "bowl"))
+            type = bowl;
+         else if(!fstrcmp(extension, "o"))
+            type = o;
+      }
+      return type;
+   }
+};
+
 class ProjectNode : ListItem
 {
 public:
@@ -208,7 +274,7 @@ public:
       }
       isset { return nodeType == folder; }
    };
-   property String fileName
+   property const String fileName
    {
       set
       {
@@ -248,7 +314,7 @@ public:
    property ProjectOptions options
    {
       get { return project ? project.options : options; }
-      set { if(project) project.options = value; else options = value; }
+      set { if(project) { delete project.options; project.options = value; } else { delete options; options = value; } }
       isset { ProjectOptions options = project ? project.options : this.options; return options && !options.isEmpty; }
    }
    property Array<PlatformOptions> platforms
@@ -290,7 +356,15 @@ public:
       get { return project ? project.configurations : configurations; }
       set
       {
-         if(project) { project.configurations = value; }
+         if(project)
+         {
+            if(project.configurations)
+            {
+               project.configurations.Free();
+               delete project.configurations;
+            }
+            project.configurations = value;
+         }
          else
          {
             if(configurations) { configurations.Free(); delete configurations; }
@@ -348,34 +422,65 @@ public:
       }
    }
 
-   property ProjectConfig config
+private:
+   ProjectOptions options;
+   Array<PlatformOptions> platforms;
+   List<ProjectConfig> configurations;
+   ProjectNodeType nodeType;
+   ProjectNode parent;
+   char * name;
+   char * info;
+
+   // This holds the absolute path of the .epj for the project topnode (without the filename)
+   // It holds a relative path to the topNode (project) for other nodes (folders and files)
+   // For folders, it includes the folder it refers to. If there is a name difference between the
+   // file system folder and the grouping folder of the project view, it maps to that folder.
+   char * path;
+
+   NodeTypes type;
+   NodeIcons icon;
+   int indent;
+   DataRow row;
+
+   bool modified;
+
+   // This is only set for Top Nodes
+   Project project;
+
+   void OpenRulesPlatformExclusionIfs(File f, int * ifCount, Array<Platform> platforms)
    {
-      get
-      {
-         Project prj;
-         ProjectConfig result = null;
-         if(configurations && (prj = property::project) && prj.config)
+      if(!platforms.Find(unknown))  // unknown is "Common"
+      {
+         // e.g. ifneq ($(or $(or $(OSX_TARGET),$(LINUX_TARGET)),$(WINDOWS_TARGET)),)
+         int i = 0;
+         f.Puts("ifneq (");
+         for(i = 0; platforms.count && i < platforms.count - 1; i++)
+            f.Puts("$(or ");
+         i = 0;
+         for(p : platforms)
          {
-            const char * projectConfigName = prj.config.name;
-            for(config : configurations)
-            {
-               if(!strcmpi(config.name, projectConfigName))
-               {
-                  result = config;
-                  break;
-               }
-            }
+            if(i > 0)
+               f.Puts(",");
+            f.Puts("$(");
+            f.Puts(PlatformToMakefileTargetVariable(p));
+            f.Puts(")");
+            if(i > 0)
+               f.Puts(")");
+            i++;
          }
-         return result;
+         f.Puts(",)\n");
+         (*ifCount)++;
       }
+      else
+         *ifCount = 0;
    }
 
-   ProjectConfig GetMatchingNodeConfig(ProjectConfig config)
+   ProjectConfig GetMatchingNodeConfig(ProjectConfig prjConfig)
    {
       ProjectConfig nodeConfig = null;
-      if(property::configurations)
+      if(property::configurations && prjConfig)
       {
-         const char * configName = config.name;
+         const char * configName = prjConfig.name;
          for(cfg : property::configurations)
          {
             if(!strcmpi(cfg.name, configName))
@@ -388,93 +493,13 @@ public:
       return nodeConfig;
    }
 
-   property bool ecflags
-   {
-      get
-      {
-         ProjectConfig config = this.config;
-         ProjectOptions options = property::options;
-         SetBool memoryGuard = localMemoryGuard;
-         String defaultNameSpace = localDefaultNameSpace;
-         SetBool strictNameSpaces = localStrictNameSpaces;
-         SetBool noLineNumbers = localNoLineNumbers;
-
-         if(memoryGuard || defaultNameSpace || strictNameSpaces || noLineNumbers)
-            return true;
-         else if(parent.parent)
-            return parent.ecflags;
-         else
-            return false;
-      }
-   }
-   property bool memoryGuard
-   {
-      get
-      {
-         ProjectConfig config = this.config;
-         ProjectOptions options = property::options;
-         SetBool memoryGuard = localMemoryGuard;
-         if(!memoryGuard)
-         {
-            if(parent)
-               return parent.memoryGuard;
-         }
-         return memoryGuard == true;
-      }
-   }
-   property String defaultNameSpace
-   {
-      get
-      {
-         ProjectConfig config = this.config;
-         ProjectOptions options = property::options;
-         String defaultNameSpace = localDefaultNameSpace;
-         if(!defaultNameSpace)
-         {
-            if(parent)
-               return parent.defaultNameSpace;
-         }
-         return defaultNameSpace;
-      }
-   }
-   property bool strictNameSpaces
-   {
-      get
-      {
-         ProjectConfig config = this.config;
-         ProjectOptions options = property::options;
-         SetBool strictNameSpaces = localStrictNameSpaces;
-         if(!strictNameSpaces)
-         {
-            if(parent)
-               return parent.strictNameSpaces;
-         }
-         return strictNameSpaces == true;
-      }
-   }
-   property bool noLineNumbers
-   {
-      get
-      {
-         ProjectConfig config = this.config;
-         ProjectOptions options = property::options;
-         SetBool noLineNumbers = localNoLineNumbers;
-         if(!noLineNumbers)
-         {
-            if(parent)
-               return parent.noLineNumbers;
-         }
-         return noLineNumbers == true;
-      }
-   }
-
    property ProjectNode root { get { ProjectNode n; for(n = this; n.parent; n = n.parent); return n; } }
 
    property bool containsFile
    {
       get
       {
-         bool result;
+         bool result = false;
          if(files)
          {
             for(child : files)
@@ -493,13 +518,82 @@ public:
       }
    }
 
-   char * GetFullFilePath(char * buffer)
+   char * GetFullFilePath(char * buffer, bool resolveVars)
    {
       if(buffer)
       {
          strcpy(buffer, root.path);
-         PathCatSlash(buffer, path);
-         PathCatSlash(buffer, name);
+         if(resolveVars)
+         {
+            if(path && path[0])
+            {
+               DirExpression pathExp { };
+               Project project = property::project;
+               CompilerConfig compiler = GetCompilerConfig();
+               ProjectConfig config = project.config;
+               int bitDepth = GetBitDepth();
+               pathExp.Evaluate(path, project, compiler, config, bitDepth);
+               PathCatSlash(buffer, pathExp.dir);
+               delete compiler;
+               delete pathExp;
+            }
+            PathCatSlash(buffer, name);
+         }
+         else
+         {
+            PathCatSlash(buffer, path);
+            PathCatSlash(buffer, name);
+         }
+      }
+      return buffer;
+   }
+
+   char * GetObjectFileName(char * buffer, Map<String, NameCollisionInfo> namesInfo, IntermediateFileType type, bool dotMain, const char * objectFileExt)
+   {
+      if(buffer && (this.type == file || (this.type == project && dotMain == true)))
+      {
+         bool collision;
+         char extension[MAX_EXTENSION];
+         char moduleName[MAX_FILENAME];
+         const char * objFileExt = objectFileExt ? objectFileExt : objectDefaultFileExt;
+         NameCollisionInfo info;
+
+         GetExtension(name, extension);
+         ReplaceSpaces(moduleName, name);
+         StripExtension(moduleName);
+         info = namesInfo[moduleName];
+         collision = info ? info.IsExtensionColliding(extension) : false;
+
+         if(dotMain)
+         {
+            Project prj = property::project;
+            ReplaceSpaces(buffer, prj.moduleName);
+            StripExtension(buffer);
+            strcat(buffer, ".main.ec");
+         }
+         else
+            strcpy(buffer, name);
+         if(!strcmp(extension, "ec") || dotMain)
+         {
+            if(type == c)
+               ChangeExtension(buffer, "c", buffer);
+            else if(type == sym)
+               ChangeExtension(buffer, "sym", buffer);
+            else if(type == imp)
+               ChangeExtension(buffer, "imp", buffer);
+            else if(type == bowl)
+               ChangeExtension(buffer, "bowl", buffer);
+         }
+         if(type == o)
+         {
+            if(collision)
+            {
+               strcat(buffer, ".");
+               strcat(buffer, objFileExt);
+            }
+            else
+               ChangeExtension(buffer, objFileExt, buffer);
+         }
       }
       return buffer;
    }
@@ -523,16 +617,16 @@ public:
       return buffer;
    }
 
-   void CollectPerFileAndDirOptions(ProjectConfig projectConfig, Array<String> perFilePreprocessorDefs, Array<DirPath> perFileIncludeDirs)
+   void CollectPerFileAndDirOptions(ProjectConfig prjConfig, Array<String> perFilePreprocessorDefs, Array<DirPath> perFileIncludeDirs)
    {
       ProjectNode node = null;
-      ProjectConfig config = GetMatchingNodeConfig(projectConfig);
+      ProjectConfig config = GetMatchingNodeConfig(prjConfig);
       List<ProjectNode> nodeStack { };
-      
+
       for(node = this; node && node.parent; node = node.parent)
          nodeStack.Add(node);
 
-      // Should we reverse this stack to give priority to the per-file includes? Does the following technique already reverse? 
+      // Should we reverse this stack to give priority to the per-file includes? Does the following technique already reverse?
 
       // TODO: Check how to fix duplication of following options when configuration is made per-config-per-file
       while((node = nodeStack.lastIterator.data))
@@ -563,30 +657,6 @@ public:
       delete nodeStack;
    }
 
-private:
-   ProjectOptions options;
-   Array<PlatformOptions> platforms;
-   List<ProjectConfig> configurations;
-   ProjectNodeType nodeType;
-   ProjectNode parent;
-   char * name;
-   char * info;
-
-   // This holds the absolute path of the .epj for the project topnode (without the filename)
-   // It holds a relative path to the topNode (project) for other nodes (folders and files)
-   // For folders, it includes the folder it refers to. If there is a name difference between the
-   // file system folder and the grouping folder of the project view, it maps to that folder.
-   char * path;
-   
-   NodeTypes type;
-   NodeIcons icon;
-   int indent;
-   DataRow row;
-
-   bool modified;
-   
-   // This is only set for Top Nodes
-   Project project;
 
    property Project project
    {
@@ -596,9 +666,9 @@ private:
          while(n && n.type != project) n = n.parent;
          return n ? (*&n.project) : null;
       }
-   }   
+   }
 
-   void RenameConfig(char * oldName, char * newName)
+   void RenameConfig(const char * oldName, const char * newName)
    {
       if(files)
       {
@@ -627,7 +697,7 @@ private:
          {
             ProjectConfig config = c.data;
             if(!strcmp(configToDelete.name, config.name))
-            {               
+            {
                c.Remove();
                delete config;
                break;
@@ -746,7 +816,7 @@ private:
       }
    }
 
-   char * OnGetString(char * tempString, void * fieldData, bool * needClass)
+   const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
    {
       if(!needClass)
       {
@@ -807,149 +877,155 @@ private:
       }
    }
 
-   property TwoStrings platformSpecificFu
+   TwoStrings GetPlatformSpecificFu(ProjectConfig prjConfig)
    {
-      get
-      {
-         TwoStrings result { a = CopyString(""), b = CopyString("") };
-         // note: unknown platform is for common
-         Map<Platform, SetBool> exclusionInfo { };
-         MapNode<Platform, SetBool> mn;
-         char * exp, * var;
-         int len;
-         SetBool common;
+      TwoStrings result { a = CopyString(""), b = CopyString("") };
+      // note: unknown platform is for common
+      Map<Platform, SetBool> exclusionInfo { };
+      MapNode<Platform, SetBool> mn;
+      char * exp;
+      const char * var;
+      int len;
+      SetBool common;
 
-         CollectExclusionInfo(exclusionInfo);
-         common = exclusionInfo[unknown];
+      CollectExclusionInfo(exclusionInfo, prjConfig);
+      common = exclusionInfo[unknown];
+      {
+         Map<Platform, SetBool> cleaned { };
+         SetBool opposite = common == true ? false : true;
+         for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
          {
-            Map<Platform, SetBool> cleaned { };
-            SetBool opposite = common == true ? false : true;
-            for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
-            {
-               if(mn.key == unknown || mn.value == opposite)
-                 cleaned[mn.key] = mn.value;
-            }
-            delete exclusionInfo;
-            exclusionInfo = cleaned;
+            if(mn.key == unknown || mn.value == opposite)
+              cleaned[mn.key] = mn.value;
          }
+         delete exclusionInfo;
+         exclusionInfo = cleaned;
+      }
 
-         if(exclusionInfo.count > 1)
+      if(exclusionInfo.count > 1)
+      {
+         if(exclusionInfo.count > 2)
          {
-            if(exclusionInfo.count > 2)
-            {
-               exp = result.a;
-               len = strlen(exp) + strlen("$(if $(or ");
-               exp = renew exp char[len+1];
-               strcat(exp, "$(if $(or ");
-               result.a = exp;
+            exp = result.a;
+            len = strlen(exp) + strlen("$(if $(or ");
+            exp = renew exp char[len+1];
+            strcat(exp, "$(if $(or ");
+            result.a = exp;
 
-               for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
+            for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
+            {
+               if(mn.key != unknown)
                {
-                  if(mn.key != unknown)
-                  {
-                     char * comma = mn.next ? "," : "";
-
-                     var = PlatformToMakefileVariable(mn.key);
-
-                     exp = result.a;
-                     len = strlen(exp) + strlen("$(") + strlen(var) + strlen(")") + strlen(comma);
-                     exp = renew exp char[len+1];
-                     strcat(exp, "$(");
-                     strcat(exp, var);
-                     strcat(exp, ")");
-                     strcat(exp, comma);
-                     result.a = exp;
-                  }
+                  const char * comma = mn.next ? "," : "";
+
+                  var = PlatformToMakefileTargetVariable(mn.key);
+
+                  exp = result.a;
+                  len = strlen(exp) + strlen("$(") + strlen(var) + strlen(")") + strlen(comma);
+                  exp = renew exp char[len+1];
+                  strcat(exp, "$(");
+                  strcat(exp, var);
+                  strcat(exp, ")");
+                  strcat(exp, comma);
+                  result.a = exp;
                }
-
-               exp = result.a;
-               len = strlen(exp) + strlen("),");
-               exp = renew exp char[len+1];
             }
-            else
-            {
-               if(exclusionInfo.root.minimum.key != unknown)
-                  var = PlatformToMakefileVariable(exclusionInfo.root.minimum.key);
-               else
-                  var = PlatformToMakefileVariable(exclusionInfo.root.minimum.next.key);
-
-               exp = result.a;
-               len = strlen(exp) + strlen("$(if $(") + strlen(var) + strlen("),");
-               exp = renew exp char[len+1];
-               strcat(exp, "$(if $(");
-               strcat(exp, var);
-            }
-
-            strcat(exp, "),");
-            result.a = exp;
 
-            exp = common == true ? result.b : result.a;
-            len = strlen(exp) + strlen(",");
+            exp = result.a;
+            len = strlen(exp) + strlen("),");
             exp = renew exp char[len+1];
-            strcat(exp, ",");
-            if(common == true) result.b = exp; else result.a = exp;
+         }
+         else
+         {
+            if(exclusionInfo.root.minimum.key != unknown)
+               var = PlatformToMakefileTargetVariable(exclusionInfo.root.minimum.key);
+            else
+               var = PlatformToMakefileTargetVariable(exclusionInfo.root.minimum.next.key);
 
-            exp = result.b;
-            len = strlen(exp) + strlen(")");
+            exp = result.a;
+            len = strlen(exp) + strlen("$(if $(") + strlen(var) + strlen("),");
             exp = renew exp char[len+1];
-            strcat(exp, ")");
-            result.b = exp;
+            strcat(exp, "$(if $(");
+            strcat(exp, var);
          }
-         delete exclusionInfo;
-         
-         return result;
+
+         strcat(exp, "),");
+         result.a = exp;
+
+         exp = common == true ? result.b : result.a;
+         len = strlen(exp) + strlen(",");
+         exp = renew exp char[len+1];
+         strcat(exp, ",");
+         if(common == true) result.b = exp; else result.a = exp;
+
+         exp = result.b;
+         len = strlen(exp) + strlen(")");
+         exp = renew exp char[len+1];
+         strcat(exp, ")");
+         result.b = exp;
       }
+      delete exclusionInfo;
+
+      return result;
    }
 
-   property bool isExcluded
+   bool GetIsExcluded(ProjectConfig prjConfig)
    {
-      get
+      bool result;
+      // note: unknown platform is for common
+      Map<Platform, SetBool> exclusionInfo { };
+      CollectExclusionInfo(exclusionInfo, prjConfig);
+      if(exclusionInfo.count == 0)
+         result = false;
+      else if(exclusionInfo.count == 1)
+         result = exclusionInfo.root.minimum.value == true;
+      else
       {
-         bool result;
-         // note: unknown platform is for common
-         Map<Platform, SetBool> exclusionInfo { };
-         CollectExclusionInfo(exclusionInfo);
-         if(exclusionInfo.count == 0)
-            result = false;
-         else if(exclusionInfo.count == 1)
-            result = exclusionInfo.root.minimum.value == true;
-         else
+         SetBool check = exclusionInfo.root.minimum.value;
+         MapNode<Platform, SetBool> mn;
+         for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
          {
-            SetBool check = exclusionInfo.root.minimum.value;
-            MapNode<Platform, SetBool> mn;
-            for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
-            {
-               if(check != mn.value)
-                  break;
-            }
-            if(!mn) // all are same
-               result = check == true;
-            else
-               result = false;
+            if(check != mn.value)
+               break;
          }
-         delete exclusionInfo;
-         return result;
-
+         if(!mn) // all are same
+            result = check == true;
+         else
+            result = false;
       }
+      delete exclusionInfo;
+      return result;
    }
 
-   void CollectExclusionInfo(Map<Platform, SetBool> output)
+   bool GetIsExcludedForCompiler(ProjectConfig prjConfig, CompilerConfig compiler)
+   {
+      bool result;
+      Map<Platform, SetBool> exclusionInfo { };
+      SetBool common, platform;
+      CollectExclusionInfo(exclusionInfo, prjConfig);
+      common = exclusionInfo[unknown];
+      platform = exclusionInfo[compiler.targetPlatform];
+      result = platform == true || (common == true && platform == unset);
+      delete exclusionInfo;
+      return result;
+   }
+
+   void CollectExclusionInfo(Map<Platform, SetBool> output, ProjectConfig prjConfig)
    {
       // note: unknown platform is for common
       Platform platform;
-      ProjectConfig config = property::config;
+      ProjectConfig config = GetMatchingNodeConfig(prjConfig);
       ProjectOptions options = property::options;
       Array<PlatformOptions> platforms = property::platforms;
-      List<ProjectConfig> configurations = property::configurations;
 
       if(parent)
-         parent.CollectExclusionInfo(output);
+         parent.CollectExclusionInfo(output, prjConfig);
       else
          output[unknown] = unset;
 
       if(options && options.excludeFromBuild)
          output[unknown] = options.excludeFromBuild;
-      
+
       if(config && config.options && config.options.excludeFromBuild)
          output[unknown] = config.options.excludeFromBuild;
 
@@ -984,7 +1060,7 @@ private:
          parent.files.Delete(this);
    }
 
-   ProjectNode Find(char * name, bool includeResources)
+   ProjectNode Find(const char * name, bool includeResources)
    {
       ProjectNode result = null;
       if(files)
@@ -1007,7 +1083,7 @@ private:
       return result;
    }
 
-   ProjectNode FindWithPath(char * name, bool includeResources)
+   ProjectNode FindWithPath(const char * name, bool includeResources)
    {
       ProjectNode result = null;
       if(files)
@@ -1036,7 +1112,18 @@ private:
       return result;
    }
 
-   ProjectNode FindSpecial(char * name, bool recursive, bool includeResources, bool includeFolders)
+   ProjectNode FindByFullPath(const char * path, bool includeResources)
+   {
+      if(files)
+      {
+         char name[MAX_FILENAME];
+         GetLastDirectory(path, name);
+         return InternalFindByFullPath(path, includeResources, name);
+      }
+      return null;
+   }
+
+   ProjectNode InternalFindByFullPath(const char * path, bool includeResources, const char * lastDirName)
    {
       ProjectNode result = null;
       if(files)
@@ -1045,13 +1132,18 @@ private:
          {
             if(includeResources || child.type != resources)
             {
-               if((includeFolders || child.type != folder) && child.name && !strcmpi(child.name, name))
+               if(child.type != file)
+                  result = child.InternalFindByFullPath(path, includeResources, lastDirName);
+               else if(child.name && !fstrcmp(lastDirName, child.name))
                {
-                  result = child;
-                  break;
+                  char p[MAX_LOCATION];
+                  child.GetFullFilePath(p, true);
+                  if(!fstrcmp(p, path))
+                  {
+                     result = child;
+                     break;
+                  }
                }
-               if(recursive)
-                  result = child.FindSpecial(name, recursive, includeResources, includeFolders);
                if(result)
                   break;
             }
@@ -1060,28 +1152,83 @@ private:
       return result;
    }
 
-   ProjectNode FindSameNameConflict(char * name, bool includeResources, Map<Platform, SetBool> exclusionInfo)
+   ProjectNode FindByObjectFileName(const char * fileName, IntermediateFileType type, bool dotMain, Map<String, NameCollisionInfo> namesInfo, const char * objectFileExt)
    {
+      char p[MAX_LOCATION];
       ProjectNode result = null;
-      Map<Platform, SetBool> compareExclusion { };
-      SetBool common, commonComp;
-      SetBool actual, actualComp;
-      if(files)
+      if(dotMain == true && this.type == project)
       {
-         for(child : files)
+         GetObjectFileName(p, namesInfo, type, dotMain, objectFileExt);
+         if(!fstrcmp(p, fileName))
+            result = this;
+      }
+      else if(files)
+      {
+         for(child : files; child.type != resources)
          {
-            if(includeResources || child.type != resources)
+            if(child.type != file && (result = child.FindByObjectFileName(fileName, type, dotMain, namesInfo, objectFileExt)))
+               break;
+            else if(child.type == file && child.name)
             {
-               if(child.type != folder && child.name && !strcmpi(child.name, name))
+               child.GetObjectFileName(p, namesInfo, type, dotMain, objectFileExt);
+               if(!fstrcmp(p, fileName))
                {
-                  child.CollectExclusionInfo(compareExclusion);
-                  common = exclusionInfo[unknown];
-                  commonComp = compareExclusion[unknown];
-                  if(exclusionInfo.count == 1 && compareExclusion.count == 1)
-                  {
-                     if(!(common == true || commonComp == true))
-                     {
-                        result = child;
+                  result = child;
+                  break;
+               }
+            }
+         }
+      }
+      return result;
+   }
+
+   ProjectNode FindSpecial(const char * name, bool recursive, bool includeResources, bool includeFolders)
+   {
+      ProjectNode result = null;
+      if(files)
+      {
+         for(child : files)
+         {
+            if(includeResources || child.type != resources)
+            {
+               if((includeFolders || child.type != folder) && child.name && !strcmpi(child.name, name))
+               {
+                  result = child;
+                  break;
+               }
+               if(recursive)
+                  result = child.FindSpecial(name, recursive, includeResources, includeFolders);
+               if(result)
+                  break;
+            }
+         }
+      }
+      return result;
+   }
+
+   ProjectNode FindSameNameConflict(const char * name, bool includeResources,
+      Map<Platform, SetBool> exclusionInfo, ProjectConfig prjConfig)
+   {
+      ProjectNode result = null;
+      Map<Platform, SetBool> compareExclusion { };
+      SetBool common, commonComp;
+      SetBool actual, actualComp;
+      if(files)
+      {
+         for(child : files)
+         {
+            if(includeResources || child.type != resources)
+            {
+               if(child.type != folder && child.name && !strcmpi(child.name, name))
+               {
+                  child.CollectExclusionInfo(compareExclusion, prjConfig);
+                  common = exclusionInfo[unknown];
+                  commonComp = compareExclusion[unknown];
+                  if(exclusionInfo.count == 1 && compareExclusion.count == 1)
+                  {
+                     if(!(common == true || commonComp == true))
+                     {
+                        result = child;
                         break;
                      }
                   }
@@ -1107,7 +1254,7 @@ private:
                   compareExclusion.Free();
                   break;
                }
-               result = child.FindSameNameConflict(name, includeResources, exclusionInfo);
+               result = child.FindSameNameConflict(name, includeResources, exclusionInfo, prjConfig);
                if(result) break;
             }
          }
@@ -1117,60 +1264,64 @@ private:
       return result;
    }
 
-   ProjectNode Add(Project project, char * filePath, ProjectNode after, NodeTypes type, NodeIcons icon, bool checkIfExists)
+   ProjectNode Add(Project project, const char * filePath, ProjectNode after, NodeTypes type, NodeIcons icon, bool checkIfExists)
    {
       ProjectNode node = null;
-      char temp[MAX_LOCATION];
-      Map<Platform, SetBool> exclusionInfo { };
-
-      GetLastDirectory(filePath, temp);
-      //if(!checkIfExists || !project.topNode.Find(temp, false))
-      CollectExclusionInfo(exclusionInfo);
-      if(!checkIfExists || !project.topNode.FindSameNameConflict(temp, false, exclusionInfo))
+      if(!project.topNode.FindByFullPath(filePath, true))
       {
-         // Do the check for folder in the same parent or resource files only here
-         if(type == folder || !checkIfExists)
+         char temp[MAX_LOCATION];
+         Map<Platform, SetBool> exclusionInfo { };
+
+         GetLastDirectory(filePath, temp);
+         //if(!checkIfExists || !project.topNode.Find(temp, false))
+
+         // TOCHECK: Shouldn't this apply either for all configs or none?
+         CollectExclusionInfo(exclusionInfo, project.config);
+         if(!checkIfExists || type == folder || !project.topNode.FindSameNameConflict(temp, false, exclusionInfo, project.config))
          {
-            for(node : files)
+            // Do the check for folder in the same parent or resource files only here
+            if(type == folder || !checkIfExists)
             {
-               if(node.name && !strcmpi(node.name, temp))
-                  return null;
+               for(node : files)
+               {
+                  if(node.name && !strcmpi(node.name, temp))
+                     return null;
+               }
             }
-         }
 
-         node = ProjectNode { parent = this, indent = indent + 1, type = type, icon = icon, name = CopyString(temp) };
-         if(type != file)
-         {
-            node.files = { }; 
-            node.nodeType = folder;
-         }
-         if(type != folder)
-         {
-            if(filePath)
+            node = ProjectNode { parent = this, indent = indent + 1, type = type, icon = icon, name = CopyString(temp) };
+            if(type != file)
             {
-               StripLastDirectory(filePath, temp);
-               MakePathRelative(temp, project.topNode.path, temp);
-               node.path = CopyUnixPath(temp);
+               node.files = { };
+               node.nodeType = folder;
             }
-            node.nodeType = file;
-         }
-         else
-         {
-            strcpy(temp, (type == NodeTypes::project) ? "" : path);
-            PathCatSlash(temp, node.name);
-            node.path = CopyString(temp);
+            if(type != folder)
+            {
+               if(filePath)
+               {
+                  StripLastDirectory(filePath, temp);
+                  MakePathRelative(temp, project.topNode.path, temp);
+                  node.path = CopyUnixPath(temp);
+               }
+               node.nodeType = file;
+            }
+            else
+            {
+               strcpy(temp, (type == NodeTypes::project) ? "" : path);
+               PathCatSlash(temp, node.name);
+               node.path = CopyString(temp);
+            }
+            files.Insert(after, node);
          }
-         files.Insert(after, node);
+         delete exclusionInfo;
       }
-      delete exclusionInfo;
       return node;
    }
 
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
    void OnDisplay(Surface surface, int x, int y, int width, ProjectView projectView, Alignment alignment, DataDisplayFlags displayFlags)
    {
       char label[MAX_FILENAME];
-      int indent = 16;
       int xStart;
       int len;
       int w, h;
@@ -1181,10 +1332,10 @@ private:
       {
          showConfig = false;
          projectView = ide.projectView;
-      }         
-      
+      }
+
       bmp = projectView.icons[icon].bitmap;
-      xStart = /*indent * indent + */x + (bmp ? (bmp.width + 5) : 0);
+      xStart = x + (bmp ? (bmp.width + 5) : 0);
 
       GetLastDirectory(name, label);
       if(!showConfig || projectView.drawingInProjectSettingsDialogHeader)
@@ -1193,8 +1344,7 @@ private:
          {
             if(projectView.projectSettingsDialog && projectView.projectSettingsDialog.buildTab)
             {
-               char * addendum;
-               addendum = projectView.projectSettingsDialog.buildTab.selectedConfigName;
+               const char * addendum = projectView.projectSettingsDialog.buildTab.selectedConfigName;
                if(strlen(addendum))
                {
                   strcat(label, " (");
@@ -1225,7 +1375,7 @@ private:
          }
       }
       len = strlen(label);
-      
+
       if(!bmp)
       {
          if(type == folder || type == folderOpen)
@@ -1236,15 +1386,15 @@ private:
       surface.TextOpacity(false);
       surface.TextExtent(label, len, &w, &h);
       h = Max(h, 16);
-    
+
       // Draw the current row stipple
       if(displayFlags.selected)
          //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
          //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
          surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
-      
+
       surface.WriteTextDots(alignment, xStart, y + 2, width, label, len);
-      
+
       if(!app.textMode)
       {
          if(displayFlags.current)
@@ -1276,7 +1426,7 @@ private:
 
    int OnCompare(ProjectNode b)
    {
-      int result;
+      int result = 0;
       if(type == b.type /*|| type >= TYPE_DRIVE*/)
          result = strcmpi(name, b.name);
       else
@@ -1287,43 +1437,38 @@ private:
       return result;
    }
 
-   void GenFileFlags(File f, Project project)
+   bool ContainsFilesWithExtension(const char * extension, ProjectConfig prjConfig)
    {
-      ProjectNode node = null;
-      List<ProjectNode> nodeStack { };
-      
-      for(node = this; node && node.parent; node = node.parent)
-         nodeStack.Add(node);
-
-      // Should we reverse this stack to give priority to the per-file includes?
-
-      while((node = nodeStack.lastIterator.data))
+      if(type == file && name && name[0])
       {
-         ProjectOptions nodeOptions = node.property::options;
-         ProjectConfig config = node.config;
-         if(nodeOptions && nodeOptions.preprocessorDefinitions)
-            OutputListOption(f, "D", nodeOptions.preprocessorDefinitions, inPlace, false);
-         if(config && config.options && config.options.preprocessorDefinitions)
-            OutputListOption(f, "D", config.options.preprocessorDefinitions, inPlace, false);
-         if(nodeOptions && nodeOptions.includeDirs)
-            OutputListOption(f, "I", nodeOptions.includeDirs, inPlace, true);
-         if(config && config.options && config.options.includeDirs)
-            OutputListOption(f, "I", config.options.includeDirs, inPlace, true);
-
-         nodeStack.lastIterator.Remove();
+         char ext[MAX_EXTENSION];
+         GetExtension(name, ext);
+         if(!fstrcmp(ext, extension))
+            return true;
       }
-      delete nodeStack;
+      else if(files)
+      {
+         for(child : files)
+         {
+            if(child.type != resources && (child.type == folder || !prjConfig || !child.GetIsExcluded(prjConfig)))
+            {
+               if(child.ContainsFilesWithExtension(extension, prjConfig))
+                  return true;
+            }
+         }
+      }
+      return false;
    }
 
-   void GenMakefileGetNameCollisionInfo(Map<String, NameCollisionInfo> namesInfo)
+   void GenMakefileGetNameCollisionInfo(Map<String, NameCollisionInfo> namesInfo, ProjectConfig prjConfig)
    {
       if(type == file)
       {
          char extension[MAX_EXTENSION];
          GetExtension(name, extension);
-         if(!strcmpi(extension, "ec") || !strcmpi(extension, "c") ||
-               !strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
-               !strcmpi(extension, "cxx") || !strcmpi(extension, "m"))
+         if(!strcmpi(extension, "ec") || !strcmpi(extension, "s") || !strcmpi(extension, "c") ||
+               !strcmpi(extension, "rc") || !strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
+               !strcmpi(extension, "cxx") || !strcmpi(extension, "m") || !strcmpi(extension, "mm"))
          {
             char moduleName[MAX_FILENAME];
             NameCollisionInfo info;
@@ -1335,8 +1480,12 @@ private:
             info.count++; // += 1; unless this is for a bug?
             if(!strcmpi(extension, "ec"))
                info.ec = true;
+            else if(!strcmpi(extension, "s"))
+               info.s = true;
             else if(!strcmpi(extension, "c"))
                info.c = true;
+            else if(!strcmpi(extension, "rc"))
+               info.rc = true;
             else if(!strcmpi(extension, "cpp"))
                info.cpp = true;
             else if(!strcmpi(extension, "cc"))
@@ -1345,6 +1494,8 @@ private:
                info.cxx = true;
             else if(!strcmpi(extension, "m"))
                info.m = true;
+            else if(!strcmpi(extension, "mm"))
+               info.mm = true;
             namesInfo[moduleName] = info;
          }
       }
@@ -1352,19 +1503,21 @@ private:
       {
          for(child : files)
          {
-            if(child.type != resources && (child.type == folder || !child.isExcluded))
-               child.GenMakefileGetNameCollisionInfo(namesInfo);
+            if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+               child.GenMakefileGetNameCollisionInfo(namesInfo, prjConfig);
          }
       }
    }
-   
-   int GenMakefilePrintNode(File f, Project project, GenMakefilePrintTypes printType, Map<String, NameCollisionInfo> namesInfo, Array<String> items)
+
+   int GenMakefilePrintNode(File f, Project project, GenMakefilePrintTypes printType,
+      Map<String, NameCollisionInfo> namesInfo, Array<String> items,
+      ProjectConfig prjConfig, bool * containsCXX)
    {
       int count = 0;
       if(type == file)
       {
          char s[2048];
-         TwoStrings ts = platformSpecificFu;
+         TwoStrings ts = GetPlatformSpecificFu(prjConfig);
          char moduleName[MAX_FILENAME];
          char extension[MAX_EXTENSION];
          GetExtension(name, extension);
@@ -1386,57 +1539,66 @@ private:
                strcpy(tempPath, path);
                PathCatSlash(tempPath, name);
             }
-            ReplaceSpaces(modulePath, tempPath);
+            EscapeForMake(modulePath, tempPath, false, true, false);
             sprintf(s, "%s%s%s%s", ts.a, useRes ? "$(RES)" : "", modulePath, ts.b);
             items.Add(CopyString(s));
          }
          else if(printType == sources)
          {
-            if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
+            if(!strcmpi(extension, "s") || !strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
                   !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
-                  !strcmpi(extension, "ec") || !strcmpi(extension, "m"))
+                  !strcmpi(extension, "m") || !strcmpi(extension, "mm"))
             {
                char modulePath[MAX_LOCATION];
-
-               ReplaceSpaces(modulePath, path);
-               ReplaceSpaces(moduleName, name);
+               EscapeForMake(modulePath, path, false, true, false);
+               EscapeForMake(moduleName, name, false, true, false);
+               sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
+               items.Add(CopyString(s));
+            }
+         }
+         else if(printType == eCsources)
+         {
+            if(!strcmpi(extension, "ec"))
+            {
+               char modulePath[MAX_LOCATION];
+               EscapeForMake(modulePath, path, true, true, false);
+               EscapeForMake(moduleName, name, true, true, false);
+               sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
+               items.Add(CopyString(s));
+               count++;
+            }
+         }
+         else if(printType == rcSources)
+         {
+            if(!strcmpi(extension, "rc"))
+            {
+               char modulePath[MAX_LOCATION];
+               EscapeForMake(modulePath, path, false, true, false);
+               EscapeForMake(moduleName, name, false, true, false);
                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
                items.Add(CopyString(s));
+               count++;
             }
          }
-         else if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
+         else if(!strcmpi(extension, "s") || !strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
-               !strcmpi(extension, "m"))
+               !strcmpi(extension, "m") || !strcmpi(extension, "mm"))
          {
             if(printType == objects)
             {
                bool collision;
                NameCollisionInfo info;
-               ReplaceSpaces(moduleName, name);
+               count++;
+               EscapeForMake(moduleName, name, false, true, false);
                StripExtension(moduleName);
                info = namesInfo[moduleName];
                collision = info ? info.IsExtensionColliding(extension) : false;
-               sprintf(s, "%s$(OBJ)%s%s%s.o%s", ts.a, moduleName, collision ? "." : "", collision ? extension : "", ts.b);
+               sprintf(s, "%s$(OBJ)%s%s%s$(O)%s", ts.a, moduleName, collision ? "." : "", collision ? extension : "", ts.b);
                items.Add(CopyString(s));
             }
-         }
-         else if(!strcmpi(extension, "ec"))
-         {
-            ReplaceSpaces(moduleName, name);
-            StripExtension(moduleName);
-            if(printType == objects)
-               count++;
-            s[0] = '\0';
-            if(printType == objects)
-               sprintf(s, "%s$(OBJ)%s.o%s", ts.a, moduleName, ts.b);
-            else if(printType == cObjects)
-               sprintf(s, "%s$(OBJ)%s.c%s", ts.a, moduleName, ts.b);
-            else if(printType == symbols)
-               sprintf(s, "%s$(OBJ)%s.sym%s", ts.a, moduleName, ts.b);
-            else if(printType == imports)
-               sprintf(s, "%s$(OBJ)%s.imp%s", ts.a, moduleName, ts.b);
-            if(s[0])
-               items.Add(CopyString(s));
+            else if(printType == noPrint && containsCXX &&
+                  (!strcmpi(extension, "cpp") || !strcmpi(extension, "cc") || !strcmpi(extension, "cxx")))
+               *containsCXX = true;
          }
          delete ts;
       }
@@ -1444,19 +1606,22 @@ private:
       {
          for(child : files)
          {
-            if(child.type != resources && (child.type == folder || !child.isExcluded))
-               count += child.GenMakefilePrintNode(f, project, printType, namesInfo, items);
+            if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+               count += child.GenMakefilePrintNode(f, project, printType, namesInfo, items, prjConfig, containsCXX);
          }
       }
       return count;
    }
 
-   void GenMakefilePrintSymbolRules(File f, Project project)
+   void GenMakefilePrintSymbolRules(File f, Project project,
+         ProjectConfig prjConfig, //Map<Platform, bool> parentExcludedPlatforms,
+         Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
    {
+      int ifCount = 0;
+      Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
       //ProjectNode child;
       //char objDir[MAX_LOCATION];
-      CompilerConfig compiler = GetCompilerConfig();
-      //ReplaceSpaces(objDir, project.config.objDir.dir);
+      //ReplaceSpaces(objDir, config.objDir.dir);
 
       //eSystem_Log("Printing Symbol Rules\n");
       if(type == file)
@@ -1464,12 +1629,12 @@ private:
          char extension[MAX_EXTENSION];
          char modulePath[MAX_LOCATION];
          char moduleName[MAX_FILENAME];
-         
+
          GetExtension(name, extension);
          if(!strcmpi(extension, "ec"))
          {
-            DualPipe dep;
-            char command[2048];
+            //DualPipe dep;
+            //char command[2048];
 
             ReplaceSpaces(moduleName, name);
             StripExtension(moduleName);
@@ -1477,6 +1642,7 @@ private:
             ReplaceSpaces(modulePath, path);
             if(modulePath[0]) strcat(modulePath, SEPS);
 
+            OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
 #if 0
             // *** Dependency command ***
             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s", moduleName,
@@ -1515,7 +1681,7 @@ private:
             }
 
             // Execute it
-            if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
+            if((dep = DualPipeOpen(PipeOpenMode { output = true, error = true/*, input = true*/ }, command)))
             {
                char line[1024];
                bool firstLine = true;
@@ -1549,70 +1715,121 @@ private:
 #endif
                   f.Printf("$(OBJ)%s.sym: %s%s.%s\n",
                      moduleName, modulePath, moduleName, extension);
+
+                  f.Puts("\t$(ECP)");
+
+                  f.Puts(" $(CFLAGS)");
+                  f.Puts(" $(CECFLAGS)"); // tocheck: what of this? should this stuff be per-file customized?
+
+                  GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
+                  GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
+
+                  f.Printf(" -c $(call quote_path,%s%s.%s) -o $(call quote_path,$@)\n",
+                     modulePath, moduleName, extension);
+                  if(ifCount) f.Puts("endif\n");
+                  f.Puts("\n");
 #if 0
                }
             }
 #endif
-         /*
-            f.Printf("\t$(ECP) %s%s.%s %s.sym\n\n",
-               modulePath, moduleName, extension, moduleName);
-            */
-
-            f.Printf("\t$(ECP)");
-            // Give priority to file flags
-            GenFileFlags(f, project);
-
-            f.Printf(" $(CECFLAGS)");
-            if(ecflags)
+         }
+      }
+      if(files)
+      {
+         if(ContainsFilesWithExtension("ec", prjConfig))
+         {
+            for(child : files)
             {
-               if(memoryGuard)
-                  f.Printf(" -memguard");
-               if(strictNameSpaces)
-                  f.Printf(" -strictns");
-               {
-                  char * s = defaultNameSpace;
-                  if(s && s[0])
-                     f.Printf(" -defaultns %s", s);
-               }
+               if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+                  child.GenMakefilePrintSymbolRules(f, project, prjConfig, /*excludedPlatforms,*/
+                        nodeCFlagsMapping, nodeECFlagsMapping);
             }
-            else
-               f.Printf(" $(ECFLAGS)");
-            f.Printf(" $(CFLAGS)");
+         }
+      }
+      delete platforms;
+   }
+
+   void GenMakefilePrintPrepecsRules(File f, Project project,
+         ProjectConfig prjConfig, /*Map<Platform, bool> parentExcludedPlatforms,*/
+         Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
+   {
+      int ifCount = 0;
+      Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
+      //ProjectNode child;
+      //char objDir[MAX_LOCATION];
+      //ReplaceSpaces(objDir, config.objDir.dir);
+
+      //eSystem_Log("Printing Symbol Rules\n");
+      if(type == file)
+      {
+         char extension[MAX_EXTENSION];
+         char modulePath[MAX_LOCATION];
+         char moduleName[MAX_FILENAME];
+
+         GetExtension(name, extension);
+         if(!strcmpi(extension, "ec"))
+         {
+            ReplaceSpaces(moduleName, name);
+            StripExtension(moduleName);
+
+            ReplaceSpaces(modulePath, path);
+            if(modulePath[0]) strcat(modulePath, SEPS);
+
+            OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
+            f.Printf("$(OBJ)%s$(EC): %s%s.%s\n",
+               moduleName, modulePath, moduleName, extension);
+            /*f.Printf("\t$(CPP) %s%s.%s %s$(S)\n\n",
+               modulePath, moduleName, extension, moduleName);*/
+
+            f.Puts("\t$(CPP)");
 
-            f.Printf(" -c %s%s.%s -o $(OBJ)%s.sym\n\n",
+            f.Puts(" $(CFLAGS)");
+            //f.Puts(" $(CECFLAGS)");
+            //GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
+            GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
+
+            f.Printf(" -x c -E %s%s.%s -o $(OBJ)%s$(EC)\n",
                modulePath, moduleName, extension, moduleName);
+            if(ifCount) f.Puts("endif\n");
+            f.Puts("\n");
          }
       }
       if(files)
       {
-         for(child : files)
+         if(ContainsFilesWithExtension("ec", prjConfig))
          {
-            // TODO: Platform specific options
-            if(child.type != resources && (child.type == folder || !child.isExcluded))
-               child.GenMakefilePrintSymbolRules(f, project);
+            for(child : files)
+            {
+               if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+                  child.GenMakefilePrintPrepecsRules(f, project, prjConfig, /*excludedPlatforms,*/
+                        nodeCFlagsMapping, nodeECFlagsMapping);
+            }
          }
       }
-      delete compiler;
+      delete platforms;
    }
 
-   void GenMakefilePrintCObjectRules(File f, Project project)
+   void GenMakefilePrintCObjectRules(File f, Project project,
+      ProjectConfig prjConfig, /*Map<Platform, bool> parentExcludedPlatforms,*/
+      Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
    {
+      int ifCount = 0;
+      Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
       //ProjectNode child;
       //char objDir[MAX_LOCATION];
-      CompilerConfig compiler = GetCompilerConfig();
-      //ReplaceSpaces(objDir, project.config.objDir.dir);
+      //ReplaceSpaces(objDir, config.objDir.dir);
       //eSystem_Log("Printing C Object Rules\n");
       if(type == file)
       {
          char extension[MAX_EXTENSION];
          char modulePath[MAX_LOCATION];
          char moduleName[MAX_FILENAME];
-         
+
          GetExtension(name, extension);
          if(!strcmpi(extension, "ec"))
          {
-            DualPipe dep;
-            char command[2048];
+            //DualPipe dep;
+            //char command[2048];
 
             ReplaceSpaces(moduleName, name);
             StripExtension(moduleName);
@@ -1620,6 +1837,7 @@ private:
             ReplaceSpaces(modulePath, path);
             if(modulePath[0]) strcat(modulePath, SEPS);
 
+            OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
 #if 0
             // *** Dependency command ***
             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s",
@@ -1639,7 +1857,7 @@ private:
                   strcat(command, item);
             }
 
-            for(item : project.config.includeDirs)
+            for(item : config.includeDirs)
             {
                strcat(command, " -I");
                if(strchr(item, ' '))
@@ -1651,14 +1869,14 @@ private:
                else
                   strcat(command, item);
             }
-            for(item : project.config.preprocessorDefs)
+            for(item : config.preprocessorDefs)
             {
                strcat(command, " -D");
                strcat(command, item);
             }
 
             // Execute it
-            if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
+            if((dep = DualPipeOpen(PipeOpenMode { output = true, error = true/*, input = true*/ }, command)))
             {
                char line[1024];
                bool result = true;
@@ -1705,48 +1923,46 @@ private:
                modulePath, moduleName, extension, moduleName);
          */
 
-            f.Printf("\t$(ECC)");
-            // Give priority to file flags
-            GenFileFlags(f, project);
-            if(ecflags)
-            {
-               f.Printf("%s $(CECFLAGS)", noLineNumbers ? " -nolinenumbers" : "");
-               if(memoryGuard)
-                  f.Printf(" -memguard");
-               if(strictNameSpaces)
-                  f.Printf(" -strictns");
-               {
-                  char * s = defaultNameSpace;
-                  if(s && s[0])
-                     f.Printf(" -defaultns %s", s);
-               }
-            }
-            else
-               f.Printf(" $(CECFLAGS) $(ECFLAGS)");
-            f.Printf(" $(CFLAGS) $(FVISIBILITY)");
+            f.Puts("\t$(ECC)");
 
-            f.Printf(" -c %s%s.%s -o $(OBJ)%s.c -symbols $(OBJ)\n\n",
-               modulePath, moduleName, extension, moduleName);
+            f.Puts(" $(CFLAGS)");
+            f.Puts(" $(CECFLAGS)"); // what of this? should this stuff be per-file customized?
+            GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
+            GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
+            f.Puts(" $(FVISIBILITY)");
+
+            f.Printf(" -c $(call quote_path,%s%s.%s) -o $(call quote_path,$@) -symbols $(OBJ)\n",
+               modulePath, moduleName, extension);
+            if(ifCount) f.Puts("endif\n");
+            f.Puts("\n");
          }
       }
       if(files)
       {
-         for(child : files)
+         if(ContainsFilesWithExtension("ec", prjConfig))
          {
-            // TODO: Platform specific options
-            if(child.type != resources && (child.type == folder || !child.isExcluded))
-               child.GenMakefilePrintCObjectRules(f, project);
+            for(child : files)
+            {
+               if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+                  child.GenMakefilePrintCObjectRules(f, project, prjConfig, /*excludedPlatforms,*/
+                        nodeCFlagsMapping, nodeECFlagsMapping);
+            }
          }
       }
-      delete compiler;
+      delete platforms;
    }
 
-   void GenMakefilePrintObjectRules(File f, Project project, Map<String, NameCollisionInfo> namesInfo)
+   void GenMakefilePrintObjectRules(File f, Project project,
+      Map<String, NameCollisionInfo> namesInfo,
+      ProjectConfig prjConfig,
+      //Map<Platform, bool> parentExcludedPlatforms,
+      Map<intptr, int> nodeCFlagsMapping, Map<intptr, int> nodeECFlagsMapping)
    {
+      int ifCount = 0;
+      Array<Platform> platforms = GetPlatformsArrayFromExclusionInfo(prjConfig);
       //ProjectNode child;
       //char objDir[MAX_LOCATION];
-      CompilerConfig compiler = GetCompilerConfig();
-      //ReplaceSpaces(objDir, project.config.objDir.dir);
+      //ReplaceSpaces(objDir, config.objDir.dir);
       //eSystem_Log("Printing Object Rules\n");
       if(type == file)
       {
@@ -1754,17 +1970,14 @@ private:
          char extension[MAX_EXTENSION];
          char modulePath[MAX_LOCATION];
          char moduleName[MAX_FILENAME];
-         
+
          GetExtension(name, extension);
-         /*if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
-               !strcmpi(extension, "ec") || !strcmpi(extension, "cc") ||
-               !strcmpi(extension, "cxx"))*/
-         if((!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
-               !strcmpi(extension, "cc") || !strcmpi(extension, "cxx")) ||
-               !strcmpi(extension, "ec"))
-         {
-            DualPipe dep;
-            char command[2048];
+         if(!strcmpi(extension, "s") || !strcmpi(extension, "c") || !strcmpi(extension, "rc") ||
+               !strcmpi(extension, "cpp") || !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
+               !strcmpi(extension, "m") || !strcmpi(extension, "mm") || !strcmpi(extension, "ec"))
+         {
+            //DualPipe dep;
+            //char command[2048];
             NameCollisionInfo info;
 
             ReplaceSpaces(moduleName, name);
@@ -1772,21 +1985,25 @@ private:
 
             info = namesInfo[moduleName];
             collision = info ? info.IsExtensionColliding(extension) : false;
-            
+
             ReplaceSpaces(modulePath, path);
             if(modulePath[0]) strcat(modulePath, SEPS);
 
+            /*
+#if 0
             // *** Dependency command ***
             if(!strcmpi(extension, "ec"))
-               sprintf(command, "%s -MT $(OBJ)%s.o -MM $(OBJ)%s.c", compiler.ccCommand, moduleName, moduleName);
+               sprintf(command, "%s -MT $(OBJ)%s.o -MM $(OBJ)%s.c", "$(CPP)", moduleName, moduleName);
             else
-               sprintf(command, "%s -MT $(OBJ)%s.o -MM %s%s.%s", compiler.ccCommand, moduleName, modulePath, moduleName, extension);
+               sprintf(command, "%s -MT $(OBJ)%s.o -MM %s%s.%s", (!strcmpi(extension, "cc") || !strcmpi(extension, "cxx") || !strcmpi(extension, "cpp")) ? "$(CXX)" : "$(CC)",
+                  moduleName, modulePath, moduleName, extension);
 
             if(!strcmpi(extension, "ec"))
+            {
                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
+            }
             else
             {
-#if 0
                // System Includes (from global settings)
                for(item : compiler.dirs[includes])
                {
@@ -1801,7 +2018,7 @@ private:
                      strcat(command, item);
                }
 
-               for(item : project.config.includeDirs)
+               for(item : config.includeDirs)
                {
                   strcat(command, " -I");
                   if(strchr(item, ' '))
@@ -1813,14 +2030,14 @@ private:
                   else
                      strcat(command, item);
                }
-               for(item : project.config.preprocessorDefs)
+               for(item : config.preprocessorDefs)
                {
                   strcat(command, " -D");
                   strcat(command, item);
                }
 
                // Execute it
-               if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
+               if((dep = DualPipeOpen(PipeOpenMode { output = true, error = true, input = false }, command)))
                {
                   char line[1024];
                   bool firstLine = true;
@@ -1852,44 +2069,73 @@ private:
                   // If we failed to generate dependencies...
                   if(!result)
                   {
-#endif
-                     /*if(!strcmpi(extension, "ec"))
-                        f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
-                     else*/
-                        f.Printf("$(OBJ)%s%s%s.o: %s%s.%s\n", moduleName, 
-                              collision ? "." : "", collision ? extension : "", modulePath, moduleName, extension);
-#if 0
+
                   }
                }
+            }
 #endif
+         */
+            if(!strcmpi(extension, "rc"))
+            {
+               ifCount++;
+               f.Puts("ifdef WINDOWS_TARGET\n\n");
             }
-            f.Printf("\t$(CC)");
-            // Give priority to file flags
-            GenFileFlags(f, project);
-
-            f.Printf(" $(CFLAGS)");
+            else
+               OpenRulesPlatformExclusionIfs(f, &ifCount, platforms);
 
             if(!strcmpi(extension, "ec"))
-               f.Printf(" $(FVISIBILITY) -c $(OBJ)%s.c -o $(OBJ)%s.o\n\n", moduleName, moduleName);
+               f.Printf("$(OBJ)%s$(O): $(OBJ)%s.c\n", moduleName, moduleName);
+            else
+               f.Printf("$(OBJ)%s%s%s$(O): %s%s.%s\n",
+                     moduleName, collision ? "." : "", collision ? extension : "",
+                     modulePath, moduleName, extension);
+            if(!strcmpi(extension, "cc") || !strcmpi(extension, "cpp") || !strcmpi(extension, "cxx"))
+               f.Printf("\t$(CXX) $(CXXFLAGS)");
+            else if(!strcmpi(extension, "rc"))
+               f.Printf("\t$(WINDRES) $(WINDRES_FLAGS) $< \"$(call escspace,$(call quote_path,$@))\"\n");
             else
-               f.Printf(" -c %s%s.%s -o $(OBJ)%s%s%s.o\n\n",
-                     modulePath, moduleName, !strcmpi(extension, "ec") ? "c" : extension, moduleName,
-                     collision ? "." : "", collision ? extension : "");
+               f.Printf("\t$(CC) $(CFLAGS)");
+
+            if(strcmpi(extension, "rc") != 0)
+            {
+               GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "PRJ_CFLAGS", f);
+
+               if(!strcmpi(extension, "ec"))
+                  f.Printf(" $(FVISIBILITY) -c $(call quote_path,$(OBJ)%s.c) -o $(call quote_path,$@)\n",
+                        moduleName);
+               else
+                  f.Printf(" -c $(call quote_path,%s%s.%s) -o $(call quote_path,$@)\n",
+                        modulePath, moduleName, !strcmpi(extension, "ec") ? "c" : extension);
+            }
+            if(ifCount) f.Puts("endif\n");
+            f.Puts("\n");
          }
       }
       if(files)
       {
+         bool needed = false;
          for(child : files)
          {
-            // TODO: Platform specific options
-            if(child.type != resources && (child.type == folder || !child.isExcluded))
-               child.GenMakefilePrintObjectRules(f, project, namesInfo);
+            if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+            {
+               needed = true;
+               break;
+            }
+         }
+         if(needed)
+         {
+            for(child : files)
+            {
+               if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+                  child.GenMakefilePrintObjectRules(f, project, namesInfo, prjConfig, /*excludedPlatforms,*/
+                        nodeCFlagsMapping, nodeECFlagsMapping);
+            }
          }
       }
-      delete compiler;
+      delete platforms;
    }
 
-   void GenMakefileAddResources(File f, String resourcesPath)
+   void GenMakefileAddResources(File f, String resourcesPath, ProjectConfig prjConfig, const char * resourcesTarget)
    {
       int count = 0;
       if(files)
@@ -1903,20 +2149,18 @@ private:
          for(c = 0; c < files.count; c++)
          {
             ProjectNode child = files[c];
-            TwoStrings ts = child.platformSpecificFu;
+            TwoStrings ts = child.GetPlatformSpecificFu(prjConfig);
             if(count > 0 && ts)
                prev = true;
-            if(child.type == file && !child.isExcluded && !(count > 0 && ts))
+            if(child.type == file && !child.GetIsExcluded(prjConfig) && !(count > 0 && ts))
             {
                bool useRes;
                char tempPath[MAX_LOCATION];
                char resPath[MAX_LOCATION];
 
-               char * quotes;
-
                // $(EAR) aw%s --- /*quiet ? "q" : */""
                if(count == 0)
-                  f.Printf("\t%s$(EAR) aw $(TARGET)", ts.a);
+                  f.Printf("\t%s$(EAR) aw$(EARFLAGS) $(%s)", ts.a, resourcesTarget);
 
                tempPath[0] = '\0';
                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
@@ -1930,12 +2174,8 @@ private:
                   strcpy(tempPath, child.path);
                   PathCatSlash(tempPath, child.name);
                }
-               ReplaceSpaces(resPath, tempPath);
-               if(strchr(tempPath, ' '))
-                  quotes = "\"";
-               else
-                  quotes = "";
-               f.Printf(" %s%s%s%s", quotes, useRes ? "$(RES)" : "", resPath, quotes);
+               EscapeForMake(resPath, tempPath, false, true, false);
+               f.Printf(" %s%s", useRes ? "$(RES)" : "", resPath);
                count++;
             }
             if(count == 10 || (count > 0 && (ts || !child.next)))
@@ -1966,30 +2206,1053 @@ private:
          for(child : files)
          {
             if(child.type == folder)
-               child.GenMakefileAddResources(f, resourcesPath);
+               child.GenMakefileAddResources(f, resourcesPath, prjConfig, resourcesTarget);
          }
       }
    }
+
+   void GenMakeCollectAssignNodeFlags(ProjectConfig prjConfig, bool prjWithEcFiles,
+         Map<String, int> cflagsVariations, Map<intptr, int> nodeCFlagsMapping,
+         Map<String, int> ecflagsVariations, Map<intptr, int> nodeECFlagsMapping,
+         Map<Platform, ProjectOptions> parentByPlatformOptions)
+   {
+      Map<Platform, ProjectOptions> byPlatformOptions = parentByPlatformOptions;
+      if(type == file || type == folder || type == project)
+      {
+         bool hasPerNodeOptions = type == project;
+         if(!hasPerNodeOptions)
+         {
+            if(options && !options.isEmpty)
+               hasPerNodeOptions = true;
+            else if(configurations)
+            {
+               for(c : configurations)
+               {
+                  if(c.options && !c.options.isEmpty)
+                  {
+                     hasPerNodeOptions = true;
+                     break;
+                  }
+                  if(c.platforms)
+                  {
+                     for(p : c.platforms)
+                     {
+                        if(p.options && !p.options.isEmpty)
+                        {
+                           hasPerNodeOptions = true;
+                           break;
+                        }
+                     }
+                     if(hasPerNodeOptions)
+                        break;
+                  }
+               }
+            }
+            if(!hasPerNodeOptions && platforms)
+            {
+               for(p : platforms)
+               {
+                  if(p.options && !p.options.isEmpty)
+                  {
+                     hasPerNodeOptions = true;
+                     break;
+                  }
+               }
+            }
+
+         }
+         if(hasPerNodeOptions)
+         {
+            bool isEqual = false, isGreater = false;
+            ComplexComparison complexCmp;
+            DynamicString s;
+            Map<Platform, ProjectOptions> additionsByPlatformOptions { };
+            ProjectOptions platformsCommonOptions;
+            ProjectOptions byFileConfigPlatformProjectOptions;
+
+            DynamicString cflags { };
+            DynamicString ecflags { };
+
+            Platform platform;
+
+            byPlatformOptions = { };
+
+            for(platform = (Platform)1; platform < Platform::enumSize; platform++)
+            {
+               byFileConfigPlatformProjectOptions =
+                     BlendFileConfigPlatformProjectOptions(this, prjConfig, platform);
+               byPlatformOptions[platform] = byFileConfigPlatformProjectOptions;
+            }
+
+            CollectPlatformsCommonOptions(byPlatformOptions, &platformsCommonOptions);
+
+            byPlatformOptions[unknown] = platformsCommonOptions;
+
+            if(parentByPlatformOptions)
+            {
+               complexCmp = PlatformsOptionsGreaterEqual(byPlatformOptions,
+                     parentByPlatformOptions, additionsByPlatformOptions);
+               isGreater = complexCmp == greater;
+               isEqual = complexCmp == equal;
+            }
+
+            if(!isEqual)
+            {
+               for(platform = (Platform)1; platform < Platform::enumSize; platform++)
+               {
+                  byFileConfigPlatformProjectOptions = isGreater ? additionsByPlatformOptions[platform] : byPlatformOptions[platform];
+                  s = { };
+                  GenCFlagsFromProjectOptions(byFileConfigPlatformProjectOptions, prjWithEcFiles, false, isGreater, s);
+                  if(s.count > 1)
+                     cflags.concatf(" \\\n\t $(if $(%s),%s,)", PlatformToMakefileTargetVariable(platform), (String)s);
+                  delete s;
+                  s = { };
+                  GenECFlagsFromProjectOptions(byFileConfigPlatformProjectOptions, prjWithEcFiles, s);
+                  if(s.count > 1)
+                     ecflags.concatf(" \\\n\t $(if $(%s),%s,)", PlatformToMakefileTargetVariable(platform), (String)s);
+                  delete s;
+               }
+
+               platformsCommonOptions = isGreater ? additionsByPlatformOptions[unknown] : byPlatformOptions[unknown];
+               s = { };
+               GenCFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, true, isGreater, s);
+               if(s.count > 1)
+               {
+                  if(!isGreater) cflags.concat(" \\\n\t");
+                  cflags.concat(s);
+               }
+               delete s;
+               s = { };
+               GenECFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, s);
+               if(s.count > 1)
+               {
+                  ecflags.concat(" \\\n\t");
+                  ecflags.concat(s);
+               }
+               delete s;
+
+               if(isGreater)
+               {
+                  cflags.concat(" \\\n\t");
+                  DynStringPrintNodeFlagsVariable(parent, nodeCFlagsMapping, "PRJ_CFLAGS", cflags);
+               }
+            }
+
+            additionsByPlatformOptions.Free();
+            delete additionsByPlatformOptions;
+
+            // output
+            {
+               if(isEqual)
+               {
+                  nodeCFlagsMapping[(intptr)this] = nodeCFlagsMapping[(intptr)parent];
+                  nodeECFlagsMapping[(intptr)this] = nodeECFlagsMapping[(intptr)parent];
+               }
+               else
+               {
+                  String s;
+                  int variationNum;
+
+                  variationNum = 1;
+                  if((s = cflags) && s[0] && !(variationNum = cflagsVariations[s]))
+                     cflagsVariations[s] = variationNum = cflagsVariations.count;
+                  nodeCFlagsMapping[(intptr)this] = variationNum;
+
+                  variationNum = 1;
+                  if((s = ecflags) && s[0] && !(variationNum = ecflagsVariations[s]))
+                     ecflagsVariations[s] = variationNum = ecflagsVariations.count;
+                  nodeECFlagsMapping[(intptr)this] = variationNum;
+               }
+            }
+
+            delete cflags;
+            delete ecflags;
+         }
+         else
+         {
+            // output
+            {
+               nodeCFlagsMapping[(intptr)this] = nodeCFlagsMapping[(intptr)parent];
+               nodeECFlagsMapping[(intptr)this] = nodeECFlagsMapping[(intptr)parent];
+            }
+         }
+      }
+      if(files)
+      {
+         for(child : files)
+         {
+            if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+               child.GenMakeCollectAssignNodeFlags(prjConfig, prjWithEcFiles,
+                     cflagsVariations, nodeCFlagsMapping, ecflagsVariations, nodeECFlagsMapping,
+                     byPlatformOptions);
+         }
+      }
+
+      if(byPlatformOptions != parentByPlatformOptions)
+      {
+         byPlatformOptions.Free();
+         delete byPlatformOptions;
+      }
+   }
+
+   Array<Platform> GetPlatformsArrayFromExclusionInfo(ProjectConfig prjConfig)
+   {
+      Array<Platform> platforms { };
+      Map<Platform, SetBool> exclusionInfo { };
+      CollectExclusionInfo(exclusionInfo, prjConfig);
+
+      if(exclusionInfo[unknown] == true)
+      {
+         if(exclusionInfo.count > 1)
+            for(p : exclusionInfo; p == false)
+               platforms.Add(&p);
+      }
+      else
+      {
+         bool onlyOnknown = true;
+         for(p : exclusionInfo)
+            if(&p != unknown && p == true)
+            {
+               onlyOnknown = false;
+               break;
+            }
+         if(onlyOnknown)
+            platforms.Add(unknown);
+         else
+         {
+            Platform p;
+            for(p = unknown + 1; p < Platform::enumSize; p++)
+               if(exclusionInfo[p] != true)
+                  platforms.Add(p);
+         }
+      }
+      delete exclusionInfo;
+      return platforms;
+   }
+
+   void GetTargets(ProjectConfig prjConfig, Map<String, NameCollisionInfo> namesInfo, char * objDir, const char * objectFileExt, DynamicString output)
+   {
+      char moduleName[MAX_FILENAME];
+      if(type == file)
+      {
+         bool headerAltFailed = false;
+         bool collision;
+         char extension[MAX_EXTENSION];
+         NameCollisionInfo info;
+         Project prj = property::project;
+         Map<String, const String> headerToSource { [ { "eh", "ec" }, { "h", "c" }, { "hh", "cc" }, { "hpp", "cpp" }, { "hxx", "cxx" } ] };
+
+         GetExtension(name, extension);
+         strcpy(moduleName, name);
+         StripExtension(moduleName);
+         info = namesInfo[moduleName];
+         collision = info ? info.IsExtensionColliding(extension) : false;
+
+         for(h2s : headerToSource)
+         {
+            if(!strcmpi(extension, &h2s))
+            {
+               char filePath[MAX_LOCATION];
+               GetFullFilePath(filePath, true);
+               OutputLog($"No compilation required for header file "); OutputLog(filePath); OutputLog("\n");
+               ChangeExtension(moduleName, h2s, moduleName);
+               if(prj.topNode.Find(moduleName, false))
+               {
+                  strcpy(extension, h2s);
+                  collision = info ? info.IsExtensionColliding(extension) : false;
+                  ChangeExtension(filePath, h2s, filePath);
+                  OutputLog($"Compiling source file "); OutputLog(filePath); OutputLog($" instead\n");
+                  StripExtension(moduleName);
+               }
+               else
+               {
+                  headerAltFailed = true;
+                  OutputLog($"Unable to locate source file "); OutputLog(moduleName); OutputLog($" to compile instead of "); OutputLog(filePath); OutputLog($"\n");
+                  StripExtension(moduleName);
+               }
+               break;
+            }
+         }
+
+         if(!headerAltFailed)
+         {
+            output.concat(" \"");
+            output.concat(objDir); //.concat(" $(OBJ)");
+            output.concat("/");
+
+            if(collision)
+            {
+               strcat(moduleName, ".");
+               strcat(moduleName, extension);
+            }
+            strcat(moduleName, ".");
+            strcat(moduleName, objectFileExt);
+            output.concat(moduleName);
+            output.concat("\"");
+         }
+      }
+      else if(type == project && ContainsFilesWithExtension("ec", prjConfig))
+      {
+         Project prj = property::project;
+
+         ReplaceSpaces(moduleName, prj.moduleName);
+         strcat(moduleName, ".main.ec");
+         output.concat(" \"");
+         output.concat(objDir);
+         output.concat("/");
+         output.concat(moduleName);
+         output.concat("\"");
+
+         ChangeExtension(moduleName, "c", moduleName);
+         output.concat(" \"");
+         output.concat(objDir);
+         output.concat("/");
+         output.concat(moduleName);
+         output.concat("\"");
+
+         ChangeExtension(moduleName, "o", moduleName);
+         output.concat(" \"");
+         output.concat(objDir);
+         output.concat("/");
+         output.concat(moduleName);
+         output.concat("\"");
+      }
+      else if(files)
+      {
+         for(child : files)
+         {
+            if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+               child.GetTargets(prjConfig, namesInfo, objDir, objectFileExt, output);
+         }
+      }
+   }
+
+   void DeleteIntermediateFiles(CompilerConfig compiler, ProjectConfig prjConfig, int bitDepth, Map<String, NameCollisionInfo> namesInfo, bool onlyCObject)
+   {
+      if(type == file)
+      {
+         bool collision;
+         const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
+         char extension[MAX_EXTENSION];
+         char fileName[MAX_FILENAME];
+         char moduleName[MAX_FILENAME];
+         NameCollisionInfo info;
+         Project prj = property::project;
+         DirExpression objDir = prj.GetObjDir(compiler, prjConfig, bitDepth);
+
+         GetExtension(name, extension);
+         ReplaceSpaces(moduleName, name);
+         StripExtension(moduleName);
+         info = namesInfo[moduleName];
+         collision = info ? info.IsExtensionColliding(extension) : false;
+
+         strcpy(fileName, prj.topNode.path);
+         PathCatSlash(fileName, objDir.dir);
+         PathCatSlash(fileName, name);
+
+         if(!onlyCObject && !strcmp(extension, "ec"))
+         {
+            ChangeExtension(fileName, "c", fileName);
+            if(FileExists(fileName)) DeleteFile(fileName);
+            ChangeExtension(fileName, "sym", fileName);
+            if(FileExists(fileName)) DeleteFile(fileName);
+            ChangeExtension(fileName, "imp", fileName);
+            if(FileExists(fileName)) DeleteFile(fileName);
+            ChangeExtension(fileName, "bowl", fileName);
+            if(FileExists(fileName)) DeleteFile(fileName);
+            ChangeExtension(fileName, "ec", fileName);
+         }
+
+         if(collision)
+         {
+            strcat(fileName, ".");
+            strcat(fileName, objectFileExt);
+         }
+         else
+            ChangeExtension(fileName, objectFileExt, fileName);
+         if(FileExists(fileName)) DeleteFile(fileName);
+
+         delete objDir;
+      }
+      else if(files)
+      {
+         for(child : files)
+         {
+            if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+               child.DeleteIntermediateFiles(compiler, prjConfig, bitDepth, namesInfo, onlyCObject);
+         }
+      }
+   }
+
+   bool IsInNode(ProjectNode node)
+   {
+      bool result = false;
+      ProjectNode n;
+      for(n = this; n; n = n.parent)
+      {
+         if(n == node)
+         {
+            result = true;
+            break;
+         }
+      }
+      return result;
+   }
+}
+
+// the code in this function is closely matched to OptionsBox::Load
+// and accompanying derivations of OptionBox and their use of OptionSet,
+// OptionCheck, LoadOption and FinalizeLoading methods.
+// output changing modification should be mirrored in both implementations
+static ProjectOptions BlendFileConfigPlatformProjectOptions(ProjectNode node, ProjectConfig projectConfig, Platform platform)
+{
+   ProjectOptions output { };
+
+   // legend: e Element
+   //         o Option (of a ProjectOptions)
+   //         n Node (ProjectNode)
+   //         p Platform
+   //         u Utility (GenericOptionTools)
+
+   int o;
+   int priority = 0;
+   int includeDirsOption = OPTION(includeDirs);
+   ProjectNode n;
+   const char * platformName = platform ? platform.OnGetString(0,0,0) : null;
+
+   // OPTION(ProjectOptions' last member) for size
+   Array<bool> optionConfigXplatformSet   { size = OPTION(installCommands) };
+   Array<bool> optionDone                 { size = OPTION(installCommands) };
+   Array<Array<String>> optionTempStrings { size = OPTION(installCommands) };
+
+   GenericOptionTools<SetBool>              utilSetBool {
+      bool OptionCheck(ProjectOptions options, int option) {
+         return *(SetBool*)((byte *)options + option) == true;
+      }
+      void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
+         if(options && (*(SetBool*)((byte *)options + option) == true))
+            *(SetBool*)((byte *)output + option) = true;
+      }
+   };
+   GenericOptionTools<String>               utilString {
+      void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
+         String * string = (String*)((byte *)output + option);
+         if(*string) delete *string;
+         if(options)
+            *string = CopyString(*(String*)((byte *)options + option));
+      }
+   };
+   StringArrayOptionTools                   utilStringArrays {
+      mergeValues = true;
+      caseSensitive = true;
+      bool OptionCheck(ProjectOptions options, int option) {
+         Array<String> strings = *(Array<String>*)((byte *)options + option);
+         return strings && strings.count;
+      }
+      bool OptionSet(ProjectOptions options, int option) {
+         Array<String> strings = *(Array<String>*)((byte *)options + option);
+         if(mergeValues && !configReplaces)
+            return strings && strings.count;
+         else
+            return strings != null;
+      }
+      void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
+         if(mergeValues)
+         {
+            Array<String> strings = options ? *(Array<String>*)((byte *)options + option) : null;
+            if(strings)
+            {
+               int order = 0;
+               Array<String> tempStrings = optionTempStrings[option];
+               if(!tempStrings)
+                  optionTempStrings[option] = tempStrings = { };
+               for(s : strings)
+               {
+                  bool found = false;
+                  char priorityMark[10];
+                  order++;
+                  if(priority)
+                     sprintf(priorityMark, "%06d\n", priority * 1000 + order);
+                  for(i : tempStrings; !(caseSensitive ? strcmp : strcmpi)(i, s)) { found = true; break; }
+                  if(!found) tempStrings.Add(priority ? PrintString(priorityMark, s) : CopyString(s));
+               }
+            }
+         }
+         else
+         {
+            Array<String> * newStrings = (Array<String>*)((byte *)options + option);
+            Array<String> * strings = (Array<String>*)((byte *)output + option);
+            if(*strings) { strings->Free(); delete *strings; }
+            if(*newStrings && newStrings->count) { *strings = { }; strings->Copy((void*)*newStrings); }
+         }
+      }
+      void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output) {
+         if(mergeValues)
+         {
+            Array<String> tempStrings = optionTempStrings[option];
+            Array<String> * strings = (Array<String>*)((byte *)output + option);
+            if(*strings) { strings->Free(); delete *strings; }
+            if(tempStrings && tempStrings.count) { *strings = { }; strings->Copy((void*)tempStrings); }
+            delete tempStrings;
+         }
+      }
+   };
+   GenericOptionTools<WarningsOption>       utilWarningsOption {
+      bool OptionCheck(ProjectOptions options, int option) {
+         WarningsOption value = *(WarningsOption*)((byte *)options + option);
+         return value && value != none;
+      }
+      void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
+         WarningsOption value = options ? *(WarningsOption*)((byte *)options + option) : (WarningsOption)0;
+         *(WarningsOption*)((byte *)output + option) = value;
+      }
+   };
+   GenericOptionTools<OptimizationStrategy> utilOptimizationStrategy {
+      bool OptionCheck(ProjectOptions options, int option) {
+         OptimizationStrategy value = *(OptimizationStrategy*)((byte *)options + option);
+         return value && value != none;
+      }
+      void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
+         OptimizationStrategy value = options ? *(OptimizationStrategy*)((byte *)options + option) : (OptimizationStrategy)0;
+         *(OptimizationStrategy*)((byte *)output + option) = value;
+      }
+   };
+
+   Map<int, GenericOptionTools> ot { };
+
+   // The following are compiler options
+
+   ot[OPTION(debug)] =                   utilSetBool;
+   ot[OPTION(memoryGuard)] =             utilSetBool;
+   ot[OPTION(profile)] =                 utilSetBool;
+   ot[OPTION(noLineNumbers)] =           utilSetBool;
+   ot[OPTION(strictNameSpaces)] =        utilSetBool;
+   ot[OPTION(fastMath)] =                utilSetBool;
+
+   ot[OPTION(defaultNameSpace)] =        utilString;
+
+   ot[OPTION(preprocessorDefinitions)] = utilStringArrays;
+   ot[OPTION(includeDirs)] =             utilStringArrays;
+
+   ot[OPTION(warnings)] =                utilWarningsOption;
+
+   ot[OPTION(optimization)] =            utilOptimizationStrategy;
+
+   for(n = node; n; n = n.parent)
+   {
+      ProjectConfig nodeConfig = null;
+      if(n.parent)
+         priority = (priority / 10 + 1) * 10;
+      else
+         priority = 9990;
+      if(projectConfig && n.configurations)
+      {
+         for(c : n.configurations; !strcmpi(c.name, projectConfig.name))
+         {
+            if(platform && c.platforms)
+            {
+               for(p : c.platforms; !strcmpi(p.name, platformName))
+               {
+                  for(uu : ot)
+                  {
+                     GenericOptionTools u = uu;
+                     o = &uu;
+                     if(!optionDone[o] && p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
+                     {
+                        u.LoadOption(p.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
+                        if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
+                        optionConfigXplatformSet[o] = true;
+                     }
+                  }
+                  break;
+               }
+            }
+            nodeConfig = c;
+            break;
+         }
+      }
+      for(uu : ot)
+      {
+         GenericOptionTools u = uu;
+         o = &uu;
+         if(!optionDone[o])
+         {
+            if(platform && n.platforms && (!optionConfigXplatformSet[o] || !u.configReplaces))
+            {
+               for(p : n.platforms; !strcmpi(p.name, platformName))
+               {
+                  if(p.options && (u.mergeValues ? u.OptionCheck(p.options, o) : u.OptionSet(p.options, o)))
+                  {
+                     u.LoadOption(p.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
+                     if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
+                  }
+                  break;
+               }
+            }
+            if(!optionDone[o] && nodeConfig && nodeConfig.options &&
+                  ((u.mergeValues && !u.configReplaces) ?
+                        u.OptionCheck(nodeConfig.options, o) :
+                        u.OptionSet(nodeConfig.options, o)))
+            {
+               u.LoadOption(nodeConfig.options, o, o == includeDirsOption ? priority : 0, optionTempStrings, output);
+               if(!u.mergeValues || u.configReplaces) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
+            }
+            if(!optionDone[o])
+            {
+               if(n.options && (u.mergeValues ? u.OptionCheck(n.options, o) : u.OptionSet(n.options, o)))
+               {
+                  u.LoadOption(n.options, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
+                  if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
+               }
+               else if(!n.parent)
+               {
+                  u.LoadOption(null, o, o == includeDirsOption ? priority + 1 : 0, optionTempStrings, output);
+                  if(!u.mergeValues) { u.FinalizeLoading(o, optionTempStrings, output); optionDone[o] = true; }
+               }
+            }
+         }
+      }
+   }
+   for(uu : ot)
+   {
+      GenericOptionTools u = uu;
+      o = &uu;
+      if(!optionDone[o])
+         u.FinalizeLoading(o, optionTempStrings, output);
+   }
+
+   delete optionConfigXplatformSet;
+   delete optionDone;
+   delete optionTempStrings;
+
+   delete utilSetBool;
+   delete utilString;
+   delete utilStringArrays;
+   delete utilWarningsOption;
+   delete utilOptimizationStrategy;
+
+   delete ot;
+
+   return output;
+}
+
+static void CollectPlatformsCommonOptions(Map<Platform, ProjectOptions> byPlatformOptions, ProjectOptions * platformsCommonOptions)
+{
+   ProjectOptions first = null;
+   ProjectOptions commonOptions;
+
+   Map<String, int> countIncludeDirs { };
+   Map<String, int> countPreprocessorDefinitions { };
+   Map<String, bool> commonIncludeDirs { };
+   Map<String, bool> commonPreprocessorDefinitions { };
+
+   for(options : byPlatformOptions) { first = options; break; }
+
+   *platformsCommonOptions = commonOptions = first ? first.Copy() : { };
+
+   if(commonOptions.includeDirs)
+      commonOptions.includeDirs.Free();
+   if(commonOptions.preprocessorDefinitions)
+      commonOptions.preprocessorDefinitions.Free();
+
+   for(options : byPlatformOptions)
+   {
+      if(options != first)
+      {
+         if(commonOptions.debug && options.debug != commonOptions.debug)
+            commonOptions.debug = unset;
+         if(commonOptions.memoryGuard && options.memoryGuard != commonOptions.memoryGuard)
+            commonOptions.memoryGuard = unset;
+         if(commonOptions.profile && options.profile != commonOptions.profile)
+            commonOptions.profile = unset;
+         if(commonOptions.noLineNumbers && options.noLineNumbers != commonOptions.noLineNumbers)
+            commonOptions.noLineNumbers = unset;
+         if(commonOptions.strictNameSpaces && options.strictNameSpaces != commonOptions.strictNameSpaces)
+            commonOptions.strictNameSpaces = unset;
+         if(commonOptions.fastMath && options.fastMath != commonOptions.fastMath)
+            commonOptions.fastMath = unset;
+
+         if(commonOptions.warnings && options.warnings != commonOptions.warnings)
+            commonOptions.warnings = unset;
+         if(commonOptions.optimization && options.optimization != commonOptions.optimization)
+            commonOptions.optimization = unset;
+
+         if(commonOptions.defaultNameSpace && strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
+            delete commonOptions.defaultNameSpace;
+      }
+
+      CountSameNonEmptyOrNullStrings(options.includeDirs, countIncludeDirs);
+      CountSameNonEmptyOrNullStrings(options.preprocessorDefinitions, countPreprocessorDefinitions);
+   }
+
+   GetPlatformsCommonStrings(countIncludeDirs, byPlatformOptions.count,
+         commonIncludeDirs, commonOptions.includeDirs);
+   GetPlatformsCommonStrings(countPreprocessorDefinitions, byPlatformOptions.count,
+         commonPreprocessorDefinitions, commonOptions.preprocessorDefinitions);
+
+   for(options : byPlatformOptions)
+   {
+      if(options.debug && options.debug == commonOptions.debug)
+         options.debug = unset;
+      if(options.memoryGuard && options.memoryGuard == commonOptions.memoryGuard)
+         options.memoryGuard = unset;
+      if(options.profile && options.profile == commonOptions.profile)
+         options.profile = unset;
+      if(options.noLineNumbers && options.noLineNumbers == commonOptions.noLineNumbers)
+         options.noLineNumbers = unset;
+      if(options.strictNameSpaces && options.strictNameSpaces == commonOptions.strictNameSpaces)
+         options.strictNameSpaces = unset;
+      if(options.fastMath && options.fastMath == commonOptions.fastMath)
+         options.fastMath = unset;
+
+      if(options.warnings && options.warnings == commonOptions.warnings)
+         options.warnings = unset;
+      if(options.optimization && options.optimization == commonOptions.optimization)
+         options.optimization = unset;
+
+      if(options.defaultNameSpace && !strcmp(options.defaultNameSpace, commonOptions.defaultNameSpace))
+         delete options.defaultNameSpace;
+
+      RemovePlatformsCommonStrings(commonIncludeDirs, options.includeDirs);
+      RemovePlatformsCommonStrings(commonPreprocessorDefinitions, options.preprocessorDefinitions);
+   }
+
+   delete countIncludeDirs;
+   delete countPreprocessorDefinitions;
+   delete commonIncludeDirs;
+   delete commonPreprocessorDefinitions;
+}
+
+static ComplexComparison PlatformsOptionsGreaterEqual(Map<Platform, ProjectOptions> byPlatformOptions,
+      Map<Platform, ProjectOptions> parentByPlatformOptions,
+      Map<Platform, ProjectOptions> additionsByPlatformOptions)
+{
+   ComplexComparison result = equal;
+   ComplexComparison compare;
+   Platform platform;
+   for(platform = (Platform)0; platform < Platform::enumSize; platform++)
+   {
+      ProjectOptions additionalOptions;
+      additionsByPlatformOptions[platform] = { };
+      additionalOptions = additionsByPlatformOptions[platform];
+      compare = ExtractPlatformsOptionsAdditions(byPlatformOptions[platform], parentByPlatformOptions[platform], additionalOptions);
+      if(compare == greater && result == equal)
+         result = greater;
+      else if(compare == different)
+      {
+         result = different;
+         break;
+      }
+   }
+   return result;
+}
+
+static ComplexComparison ExtractPlatformsOptionsAdditions(ProjectOptions options, ProjectOptions parentOptions, ProjectOptions additionalOptions)
+{
+   ComplexComparison result = equal;
+   if(options.debug != parentOptions.debug ||
+         options.memoryGuard != parentOptions.memoryGuard ||
+         options.profile != parentOptions.profile ||
+         options.noLineNumbers != parentOptions.noLineNumbers ||
+         options.strictNameSpaces != parentOptions.strictNameSpaces ||
+         options.fastMath != parentOptions.fastMath ||
+         options.warnings != parentOptions.warnings ||
+         options.optimization != parentOptions.optimization ||
+         (options.defaultNameSpace != parentOptions.defaultNameSpace &&
+               strcmp(options.defaultNameSpace, parentOptions.defaultNameSpace)))
+      result = different;
+   else
+   {
+      if(!StringsAreSameOrMore(options.includeDirs, parentOptions.includeDirs, &additionalOptions.includeDirs) ||
+            !StringsAreSameOrMore(options.preprocessorDefinitions, parentOptions.preprocessorDefinitions, &additionalOptions.preprocessorDefinitions))
+         result = different;
+      if((additionalOptions.includeDirs && additionalOptions.includeDirs.count) ||
+            (additionalOptions.preprocessorDefinitions && additionalOptions.preprocessorDefinitions.count))
+         result = greater;
+   }
+   return result;
+}
+
+enum ComplexComparison { different/*, smaller*/, equal, greater };
+
+static bool StringsAreSameOrMore(Array<String> strings, Array<String> originals, Array<String> * additions)
+{
+   bool result = true;
+   if((!strings || !strings.count) && originals && originals.count)
+      result = false;
+   else if(strings && strings.count && (!originals || !originals.count))
+   {
+      if(!*additions)
+         *additions = { };
+      for(s : strings)
+         additions->Add(CopyString(s));
+   }
+   else if(strings && strings.count && originals && originals.count)
+   {
+      Map<String, String> map { };
+      MapIterator<String, bool> mit { map = map };
+      for(it : strings)
+      {
+         char * s = strstr(it, "\n");
+         s = s ? s+1 : it;
+         map[s] = it;
+      }
+      for(it : originals)
+      {
+         char * s = strstr(it, "\n");
+         s = s ? s+1 : it;
+         if(!mit.Index(s, false))
+         {
+            result = false;
+            break;
+         }
+         else
+            mit.Remove();
+            // TOFIX: Templates map[s] = null;
+      }
+      if(result)
+      {
+         if(!*additions)
+            *additions = { };
+         for(it : map)
+         {
+            if(it)
+               additions->Add(CopyString(it));
+         }
+      }
+      delete map;
+   }
+   return result;
+}
+
+static void CountSameNonEmptyOrNullStrings(Array<String> strings, Map<String, int> counts)
+{
+   if(strings)
+   {
+      for(it : strings)
+      {
+         char * s = it;
+         if(s && s[0])
+            counts[s]++;
+      }
+   }
+}
+
+static void GetPlatformsCommonStrings(Map<String, int> counts, int goodCount, Map<String, bool> common, Array<String> strings)
+{
+   for(it : counts)
+   {
+      int i = it;
+      if(i == goodCount)
+      {
+         const char * s = &it;
+         strings.Add(CopyString(s));
+         common[s] = true;
+      }
+   }
+}
+
+static void RemovePlatformsCommonStrings(Map<String, bool> common, Array<String> strings)
+{
+   if(strings)
+   {
+      Array<String> tmp { };
+      MapIterator<String, bool> mit { map = common };
+      for(it : strings)
+      {
+         char * s = it;
+         if(!mit.Index(s, false))
+            tmp.Add(CopyString(s));
+      }
+      strings.Free();
+      if(tmp.count)
+      {
+         for(s : tmp)
+            strings.Add(CopyString(s));
+         tmp.Free();
+      }
+      delete tmp;
+   }
+}
+
+static void GenMakePrintNodeFlagsVariable(ProjectNode node, Map<intptr, int> nodeFlagsMapping, const String variableName, File f)
+{
+   int customFlags;
+   customFlags = nodeFlagsMapping[(intptr)node];
+   if(customFlags > 1)
+      f.Printf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
+   else
+      f.Printf(" $(%s)", variableName);
+}
+
+static void DynStringPrintNodeFlagsVariable(ProjectNode node, Map<intptr, int> nodeFlagsMapping, const String variableName, DynamicString s)
+{
+   int customFlags;
+   customFlags = nodeFlagsMapping[(intptr)node];
+   if(customFlags > 1)
+      s.concatf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
+   else
+      s.concatf(" $(%s)", variableName);
+}
+
+static void GenCFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, bool commonOptions, bool isGreater, DynamicString s)
+{
+   if(!isGreater)
+   {
+      //if(gccCompiler)
+      {
+         if(options.optimization == speed || options.optimization == size ||
+               options.fastMath == true || options.debug == true)
+         {
+            if(options.debug != true)
+            {
+               s.concat(" $(if $(DEBUG),");
+               s.concat(" -g");
+               s.concat(",");
+            }
+            switch(options.optimization)
+            {
+               case speed: s.concat(" -O2"); break;
+               case size: s.concat(" -Os"); break;
+            }
+            if(options.fastMath == true)
+               s.concat(" -ffast-math");
+            if(options.debug == true)
+               s.concat(" -g");
+            if(options.debug != true)
+               s.concat(")");
+         }
+         else if(commonOptions)
+            s.concat(" $(if $(DEBUG),-g)");
+         if(commonOptions)
+            s.concat(" $(FPIC)");
+      }
+      switch(options.warnings)
+      {
+         case all: s.concat(" -Wall"); break;
+         case none: s.concat(" -w"); break;
+      }
+      if(options.profile)
+         s.concat(" -pg");
+      if(commonOptions)
+         s.concat(" -DREPOSITORY_VERSION=\"\\\"$(REPOSITORY_VER)\\\"\"");
+   }
+
+   if(options && options.preprocessorDefinitions)
+      ListOptionToDynamicString(s, _D, options.preprocessorDefinitions, false, lineEach, "\t\t\t");
+   if(options && options.includeDirs)
+      ListOptionToDynamicString(s, _I, options.includeDirs, true, lineEach, "\t\t\t");
+}
+
+static void GenECFlagsFromProjectOptions(ProjectOptions options, bool prjWithEcFiles, DynamicString s)
+{
+   if(options.memoryGuard == true)
+      s.concat(" -memguard");
+   if(options.noLineNumbers == true)
+      s.concat(" -nolinenumbers");
+   if(options.strictNameSpaces == true)
+      s.concat(" -strictns");
+   if(options.defaultNameSpace && options.defaultNameSpace[0])
+      s.concatf(" -defaultns %s", options.defaultNameSpace);
+}
+
+static void ListOptionToDynamicString(DynamicString output, ToolchainFlag flag, Array<String> list, bool prioritize,
+      LineOutputMethod lineMethod, const String newLineStart)
+{
+   if(list.count)
+   {
+      if(lineMethod == newLine)
+      {
+         output.concat(" \\\n");
+         output.concat(newLineStart);
+      }
+      if(prioritize)
+      {
+         Map<String, int> sortedList { };
+         MapNode<String, int> mn;
+         for(item : list)
+            sortedList[item] = 1;
+         for(mn = sortedList.root.minimum; mn; mn = mn.next)
+         {
+            char * start = strstr(mn.key, "\n");
+            if(lineMethod == lineEach)
+            {
+               output.concat(" \\\n");
+               output.concat(newLineStart);
+            }
+            output.concat(" ");
+            output.concat(flagNames[flag]);
+            EscapeForMakeToDynString(output, start ? start+1 : mn.key, false, true, flag == _D);
+         }
+         delete sortedList;
+      }
+      else
+      {
+         for(item : list)
+         {
+            if(lineMethod == lineEach)
+            {
+               output.concat(" \\\n");
+               output.concat(newLineStart);
+            }
+            output.concat(" ");
+            output.concat(flagNames[flag]);
+            EscapeForMakeToDynString(output, item, false, true, flag == _D);
+         }
+      }
+   }
+}
+
+class GenericOptionTools<class X>
+{
+   bool mergeValues, configReplaces;
+
+   virtual bool OptionSet(ProjectOptions options, int option) {
+      if(*(X*)((byte *)options + option))
+         return true;
+      return false;
+   }
+
+   // BUG: OptionCheck = OptionSet; // Overrides derived classes OptionCheck ?
+
+   virtual bool OptionCheck(ProjectOptions options, int option) {
+      return OptionSet(options, option);
+   }
+
+   virtual void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output);
+   virtual void FinalizeLoading(int option, Array<Array<String>> optionTempStrings, ProjectOptions output);
+}
+
+class StringArrayOptionTools : GenericOptionTools<Array<String>>
+{
+   bool caseSensitive;
 }
 
 class NameCollisionInfo
 {
    bool ec;
+   bool s;
    bool c;
+   bool rc;
    bool cpp;
    bool cc;
    bool cxx;
    bool m;
+   bool mm;
    byte count;
 
    bool IsExtensionColliding(char * extension)
    {
       bool colliding;
-      if(count > 1 && ((!strcmpi(extension, "c") && ec) ||
-            (!strcmpi(extension, "cpp") && (ec || c)) ||
-            (!strcmpi(extension, "cc") && (ec || c || cpp)) ||
-            (!strcmpi(extension, "cxx") && (ec || c || cpp || cc)) ||
-            !strcmpi(extension, "m")))
+      if(count > 1 &&
+            ((!strcmpi(extension, "c")   && ec) ||
+             (!strcmpi(extension, "rc")  && (ec || c)) ||
+             (!strcmpi(extension, "s")   && (ec || c || rc)) ||
+             (!strcmpi(extension, "cpp") && (ec || c || rc || s)) ||
+             (!strcmpi(extension, "cc")  && (ec || c || rc || s || cpp)) ||
+             (!strcmpi(extension, "cxx") && (ec || c || rc || s || cpp || cc)) ||
+             (!strcmpi(extension, "m")   && (ec || c || rc || s || cpp || cc || m)) ||
+              !strcmpi(extension, "mm")))
          colliding = true;
       else
          colliding = false;