sdk/Build System: Took out extra inclusion of LDFLAGS; Moved $(CPPFLAGS) and -D_DEBUG...
[sdk] / ide / src / project / ProjectNode.ec
old mode 100644 (file)
new mode 100755 (executable)
index f260058..35d498c
@@ -12,6 +12,8 @@ import "Project"
 static define app = ((GuiApplication)__thisModule);
 #endif
 
+#define OPTION(x) ((uint)(&((ProjectOptions)0).x))
+
 bool eString_PathInsideOfMore(char * path, char * of, char * pathRest)
 {
    if(!path[0] || !of[0])
@@ -162,12 +164,6 @@ class TwoStrings : struct
    }
 }
 
-class BuildExclusionInfo : struct
-{
-   SetBool excluded;
-   Map<Platform, bool> platformSpecific { };
-}
-
 class ProjectNode : ListItem
 {
 public:
@@ -251,132 +247,158 @@ public:
    };
 
    LinkList<ProjectNode> files;
-   ProjectOptions options;
-   Array<PlatformOptions> platforms;
-   List<ProjectConfig> configurations;
-
-   property ProjectConfig config
+   property ProjectOptions options
    {
-      get
+      get { return project ? project.options : options; }
+      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
+   {
+      get { return project ? project.platforms : platforms; }
+      set
       {
-         Project prj;
-         ProjectConfig result = null;
-         /*if(configurations)
-            printf("test\n");
-         if(prj)
-            printf("test\n");*/
-         if(configurations && (prj = property::project) && prj.config)
+         if(project) { project.platforms = value; }
+         else
          {
-            const char * projectConfigName = prj.config.name;
-            for(config : configurations)
+            if(platforms) { platforms.Free(); delete platforms; }
+            if(value)
             {
-               if(!strcmpi(config.name, projectConfigName))
-               {
-                  result = config;
-                  break;
-               }
+               List<PlatformOptions> empty { };
+               Iterator<PlatformOptions> it { value };
+               platforms = value;
+               for(p : platforms; !p.options || p.options.isEmpty) empty.Add(p);
+               for(p : empty; it.Find(p)) platforms.Delete(it.pointer);
+               delete empty;
             }
          }
-         return result;
       }
-   }
-
-   ProjectConfig GetMatchingNodeConfig(ProjectConfig config)
-   {
-      ProjectConfig nodeConfig = null;
-      if(configurations)
+      isset
       {
-         const char * configName = config.name;
-         for(cfg : configurations)
+         Array<PlatformOptions> platforms = project ? project.platforms : this.platforms;
+         if(platforms)
          {
-            if(!strcmpi(cfg.name, configName))
+            for(p : platforms)
             {
-               nodeConfig = cfg;
-               break;
+               if(p.options && !p.options.isEmpty)
+                  return true;
             }
          }
-      }
-      return nodeConfig;
-   }
-
-   property bool ecflags
-   {
-      get
-      {
-         ProjectConfig config = this.config;
-         ProjectOptions options = this.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;
+         return false;
       }
    }
-   property bool memoryGuard
+   property List<ProjectConfig> configurations
    {
-      get
+      get { return project ? project.configurations : configurations; }
+      set
       {
-         ProjectConfig config = this.config;
-         ProjectOptions options = this.options;
-         SetBool memoryGuard = localMemoryGuard;
-         if(!memoryGuard)
+         if(project)
          {
-            if(parent)
-               return parent.memoryGuard;
+            if(project.configurations)
+            {
+               project.configurations.Free();
+               delete project.configurations;
+            }
+            project.configurations = value;
          }
-         return memoryGuard == true;
-      }
-   }
-   property String defaultNameSpace
-   {
-      get
-      {
-         ProjectConfig config = this.config;
-         ProjectOptions options = this.options;
-         String defaultNameSpace = localDefaultNameSpace;
-         if(!defaultNameSpace)
+         else
          {
-            if(parent)
-               return parent.defaultNameSpace;
+            if(configurations) { configurations.Free(); delete configurations; }
+            if(value)
+            {
+               List<ProjectConfig> empty { };
+               Iterator<ProjectConfig> it { value };
+               configurations = value;
+               for(c : configurations)
+               {
+                  bool somethingSet = c.options && !c.options.isEmpty;
+                  // TODO: Implement isset keyword
+                  if(!somethingSet && c.platforms && c.platforms.count)
+                  {
+                     for(p : c.platforms)
+                     {
+                        if(p.options && !p.options.isEmpty)
+                        {
+                           somethingSet = true;
+                           break;
+                        }
+                     }
+                  }
+                  if(!somethingSet)
+                     empty.Add(c);
+               }
+               for(c : empty; it.Find(c)) configurations.Delete(it.pointer);
+               delete empty;
+            }
          }
-         return defaultNameSpace;
       }
-   }
-   property bool strictNameSpaces
-   {
-      get
+      isset
       {
-         ProjectConfig config = this.config;
-         ProjectOptions options = this.options;
-         SetBool strictNameSpaces = localStrictNameSpaces;
-         if(!strictNameSpaces)
+         if(!parent) return true;
+         if(configurations)
          {
-            if(parent)
-               return parent.strictNameSpaces;
+            for(c : configurations)
+            {
+               bool somethingSet = c.options && !c.options.isEmpty;
+               if(!somethingSet && c.platforms && c.platforms.count)
+               {
+                  for(p : c.platforms)
+                  {
+                     if(p.options && !p.options.isEmpty)
+                     {
+                        somethingSet = true;
+                        break;
+                     }
+                  }
+               }
+               return somethingSet;
+            }
          }
-         return strictNameSpaces == true;
+         return false;
       }
    }
-   property bool noLineNumbers
+
+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;
+
+   ProjectConfig GetMatchingNodeConfig(ProjectConfig prjConfig)
    {
-      get
+      ProjectConfig nodeConfig = null;
+      if(property::configurations && prjConfig)
       {
-         ProjectConfig config = this.config;
-         ProjectOptions options = this.options;
-         SetBool noLineNumbers = localNoLineNumbers;
-         if(!noLineNumbers)
+         const char * configName = prjConfig.name;
+         for(cfg : property::configurations)
          {
-            if(parent)
-               return parent.noLineNumbers;
+            if(!strcmpi(cfg.name, configName))
+            {
+               nodeConfig = cfg;
+               break;
+            }
          }
-         return noLineNumbers == true;
       }
+      return nodeConfig;
    }
 
    property ProjectNode root { get { ProjectNode n; for(n = this; n.parent; n = n.parent); return n; } }
@@ -434,23 +456,25 @@ 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))
       {
-         if(node.options && node.options.preprocessorDefinitions)
+         ProjectConfig config = GetMatchingNodeConfig(prjConfig);
+         ProjectOptions nodeOptions = node.property::options;
+         if(nodeOptions && nodeOptions.preprocessorDefinitions)
          {
-            for(def : node.options.preprocessorDefinitions)
+            for(def : nodeOptions.preprocessorDefinitions)
                perFilePreprocessorDefs.Add(CopyString(def));
          }
          if(config && config.options && config.options.preprocessorDefinitions)
@@ -458,9 +482,9 @@ public:
             for(def : config.options.preprocessorDefinitions)
                perFilePreprocessorDefs.Add(CopyString(def));
          }
-         if(node.options && node.options.includeDirs)
+         if(nodeOptions && nodeOptions.includeDirs)
          {
-            for(dir : node.options.includeDirs)
+            for(dir : nodeOptions.includeDirs)
                perFileIncludeDirs.Add(CopySystemPath(dir));
          }
          if(config && config.options && config.options.includeDirs)
@@ -473,27 +497,6 @@ public:
       delete nodeStack;
    }
 
-private:
-   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
    {
@@ -511,9 +514,9 @@ private:
       {
          for(f : files; (f.configurations || f.files)) { f.RenameConfig(oldName, newName); }
       }
-      if(configurations)
+      if(property::configurations)
       {
-         for(c : configurations; !strcmp(c.name, oldName))
+         for(c : property::configurations; !strcmp(c.name, oldName))
          {
             delete c.name;
             c.name = CopyString(newName);
@@ -527,9 +530,9 @@ private:
       {
          for(f : files; (f.configurations || f.files)) { f.DeleteConfig(configToDelete); }
       }
-      if(configurations)
+      if(property::configurations)
       {
-         Iterator<ProjectConfig> c { configurations };
+         Iterator<ProjectConfig> c { property::configurations };
          while(c.Next())
          {
             ProjectConfig config = c.data;
@@ -540,10 +543,8 @@ private:
                break;
             }
          }
-         if(!configurations.count)
-         {
-            delete configurations;
-         }
+         if(!property::configurations.count)
+            property::configurations = null;
       }
    }
 
@@ -556,20 +557,20 @@ private:
          backupNode.files = { };
          for(f : files) backupNode.files.Add(f.Backup());
       }
-      if(options)
-         backupNode.options = options.Copy();
+      if(property::options)
+         backupNode.options = property::options.Copy();
 
-      if(platforms)
+      if(property::platforms)
       {
          backupNode.platforms = { };
-         for(p : platforms)
+         for(p : property::platforms)
             backupNode.platforms.Add(p.Copy());
       }
 
-      if(configurations)
+      if(property::configurations)
       {
          backupNode.configurations = { };
-         for(c : configurations)
+         for(c : property::configurations)
             backupNode.configurations.Add(c.Copy());
       }
       return backupNode;
@@ -587,28 +588,19 @@ private:
          }
       }
 
-      delete options;
-      if(platforms)
-      {
-         platforms.Free();
-         delete platforms;
-      }
-      if(configurations)
-      {
-         configurations.Free();
-         delete configurations;
-      }
-
-      options = backupNode.options ? backupNode.options.Copy() : null;
+      property::options = backupNode.options ? backupNode.options.Copy() : null;
       if(backupNode.platforms)
       {
-         platforms = { };
+         Array<PlatformOptions> platforms { };
+         property::platforms = platforms;
+
          for(p : backupNode.platforms)
             platforms.Add(p.Copy());
       }
       if(backupNode.configurations)
       {
-         configurations = { };
+         List<ProjectConfig> configurations { };
+         property::configurations = configurations;
          for(c : backupNode.configurations)
             configurations.Add(c.Copy());
       }
@@ -669,7 +661,7 @@ private:
       if(!needClass)
       {
          // TOCHECK: Called from JSON writer
-         if(nodeType == file && !options && !configurations && !platforms && name)
+         if(nodeType == file && !property::options && !property::configurations && !property::platforms && name)
          {
             strcpy(tempString, "\"");
             strcat(tempString, property::fileName);
@@ -691,14 +683,15 @@ private:
          files.Free();
          delete files;
       }
-      delete options;
+      if(!project)
+         delete options;
 
-      if(platforms)
+      if(!project && platforms)
       {
          platforms.Free();
          delete platforms;
       };
-      if(configurations)
+      if(!project && configurations)
       {
          configurations.Free();
          delete configurations;
@@ -724,33 +717,47 @@ 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;
+
+      CollectExclusionInfo(exclusionInfo, prjConfig);
+      common = exclusionInfo[unknown];
       {
-         TwoStrings result { a = CopyString(""), b = CopyString("") };
-         BuildExclusionInfo exclusion = exclusionInfo;
-         char * exp, * var;
-         int len;
-         Platform platform;
-         if(exclusion.platformSpecific.count > 1)
+         Map<Platform, SetBool> cleaned { };
+         SetBool opposite = common == true ? false : true;
+         for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
          {
-            MapNode<Platform, bool> mn;
+            if(mn.key == unknown || mn.value == opposite)
+              cleaned[mn.key] = mn.value;
+         }
+         delete exclusionInfo;
+         exclusionInfo = cleaned;
+      }
 
+      if(exclusionInfo.count > 1)
+      {
+         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;
 
-            for(mn = exclusion.platformSpecific.root.minimum; mn; mn = mn.next)
+            for(mn = exclusionInfo.root.minimum; mn; mn = mn.next)
             {
-               if(mn.value)
+               if(mn.key != unknown)
                {
                   char * comma = mn.next ? "," : "";
 
-                  platform = mn.key;
-                  var = PlatformToMakefileVariable(platform);
+                  var = PlatformToMakefileTargetVariable(mn.key);
 
                   exp = result.a;
                   len = strlen(exp) + strlen("$(") + strlen(var) + strlen(")") + strlen(comma);
@@ -766,109 +773,104 @@ private:
             exp = result.a;
             len = strlen(exp) + strlen("),");
             exp = renew exp char[len+1];
-            strcat(exp, "),");
-            result.a = exp;
-
-            exp = exclusion.excluded == true ? result.b : result.a;
-            len = strlen(exp) + strlen(",");
-            exp = renew exp char[len+1];
-            strcat(exp, ",");
-            if(exclusion.excluded == 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;
          }
-         else if(exclusion.platformSpecific.count)
+         else
          {
-            platform = exclusion.platformSpecific.root.minimum.key;
-            var = PlatformToMakefileVariable(platform);
+            if(exclusionInfo.root.minimum.key != unknown)
+               var = PlatformToMakefileTargetVariable(exclusionInfo.root.minimum.key);
+            else
+               var = PlatformToMakefileTargetVariable(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 = exclusion.excluded == true ? result.b : result.a;
-            len = strlen(exp) + strlen(",");
-            exp = renew exp char[len+1];
-            strcat(exp, ",");
-            if(exclusion.excluded == true) result.b = exp; else result.a = exp;
+         strcat(exp, "),");
+         result.a = exp;
 
-            exp = result.b;
-            len = strlen(exp) + strlen(")");
-            exp = renew exp char[len+1];
-            strcat(exp, ")");
-            result.b = exp;
-         }
-         return result;
+         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
       {
-         return exclusionInfo.excluded == true && exclusionInfo.platformSpecific.count == 0;
+         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;
       }
+      delete exclusionInfo;
+      return result;
    }
 
-   property BuildExclusionInfo exclusionInfo
+   void CollectExclusionInfo(Map<Platform, SetBool> output, ProjectConfig prjConfig)
    {
-      get
+      // note: unknown platform is for common
+      Platform platform;
+      ProjectConfig config = GetMatchingNodeConfig(prjConfig);
+      ProjectOptions options = property::options;
+      Array<PlatformOptions> platforms = property::platforms;
+      List<ProjectConfig> configurations = property::configurations;
+
+      if(parent)
+         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;
+
+      if(platforms)
       {
-         BuildExclusionInfo result { };
-         ProjectConfig config = property::config;
-         if(config && config.options && config.options.excludeFromBuild)
-            result.excluded = config.options.excludeFromBuild;
-         else if(options && options.excludeFromBuild)
-            result.excluded = options.excludeFromBuild;
-         else if(parent)
+         for(p : platforms)
          {
-            BuildExclusionInfo parentExclusion = parent.exclusionInfo;
-            if(parentExclusion.excluded)
-            {
-               result.excluded = parentExclusion.excluded;
-               if(parentExclusion.platformSpecific.count)
-               {
-                  MapNode<Platform, bool> mn;
-                  for(mn = parentExclusion.platformSpecific.root.minimum; mn; mn = mn.next)
-                  {
-                     if(mn.value)
-                     {
-                        result.platformSpecific[mn.key] = true;
-                     }
-                  }
-               }
-            }
-            if(result.excluded)
-            {
-               SetBool opposite = result.excluded == true ? false : true;
-               Platform platform;
-               if(platforms)
-               {
-                  for(p : platforms)
-                  {
-                     if(p.options.excludeFromBuild == opposite && (platform = p.name))
-                        result.platformSpecific[platform] = true;
-                  }
-               }
-               if(config && config.platforms)
-               {
-                  for(p : config.platforms)
-                  {
-                     if(p.options.excludeFromBuild == opposite && (platform = p.name))
-                        result.platformSpecific[platform] = true;
-                  }
-               }
-            }
+            if(p.options.excludeFromBuild && (platform = p.name))
+               output[platform] = p.options.excludeFromBuild;
+         }
+      }
+      if(config && config.platforms)
+      {
+         for(p : config.platforms)
+         {
+            if(p.options.excludeFromBuild && (platform = p.name))
+               output[platform] = p.options.excludeFromBuild;
          }
-         return result;
       }
    }
 
@@ -937,6 +939,34 @@ private:
       return result;
    }
 
+   ProjectNode FindByFullPath(char * path, bool includeResources)
+   {
+      ProjectNode result = null;
+      if(files)
+      {
+         for(child : files)
+         {
+            if(includeResources || child.type != resources)
+            {
+               if(child.type != folder && child.name)
+               {
+                  char p[MAX_LOCATION];
+                  child.GetFullFilePath(p);
+                  if(!strcmpi(p, path))
+                  {
+                     result = child;
+                     break;
+                  }
+               }
+               result = child.FindByFullPath(path, includeResources);
+               if(result)
+                  break;
+            }
+         }
+      }
+      return result;
+   }
+
    ProjectNode FindSpecial(char * name, bool recursive, bool includeResources, bool includeFolders)
    {
       ProjectNode result = null;
@@ -961,35 +991,98 @@ private:
       return result;
    }
 
-   ProjectNode Add(Project project, char * filePath, ProjectNode after, NodeTypes type, NodeIcons icon, bool checkIfExists)
+   ProjectNode FindSameNameConflict(char * name, bool includeResources,
+      Map<Platform, SetBool> exclusionInfo, ProjectConfig prjConfig)
    {
-      ProjectNode node = null;
-      char temp[MAX_LOCATION];
-
-      GetLastDirectory(filePath, temp);
-      if(!checkIfExists || !project.topNode.Find(temp, false))
+      ProjectNode result = null;
+      Map<Platform, SetBool> compareExclusion { };
+      SetBool common, commonComp;
+      SetBool actual, actualComp;
+      if(files)
       {
-         // Do the check for folder in the same parent or resource files only here
-         if(type == folder || !checkIfExists)
-         {
-            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)
+         for(child : files)
          {
-            if(filePath)
+            if(includeResources || child.type != resources)
             {
-               StripLastDirectory(filePath, temp);
+               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;
+                     }
+                  }
+                  else
+                  {
+                     Platform platform;
+                     for(platform = (Platform)1; platform < Platform::enumSize; platform++)
+                     {
+                        actual = common;
+                        actualComp = commonComp;
+                        if(exclusionInfo[platform] != unset)
+                           actual = exclusionInfo[platform];
+                        if(compareExclusion[platform] != unset)
+                           actualComp = compareExclusion[platform];
+                        if(!(actual == true || actualComp == true))
+                        {
+                           result = child;
+                           break;
+                        }
+                     }
+                     if(result) break;
+                  }
+                  compareExclusion.Free();
+                  break;
+               }
+               result = child.FindSameNameConflict(name, includeResources, exclusionInfo, prjConfig);
+               if(result) break;
+            }
+         }
+         compareExclusion.Free();
+      }
+      delete compareExclusion;
+      return result;
+   }
+
+   ProjectNode Add(Project project, 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))
+      
+      // TOCHECK: Shouldn't this apply either for all configs or none?
+      CollectExclusionInfo(exclusionInfo, project.config);
+      if(!checkIfExists || !project.topNode.FindSameNameConflict(temp, false, exclusionInfo, project.config))
+      {
+         // Do the check for folder in the same parent or resource files only here
+         if(type == folder || !checkIfExists)
+         {
+            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)
+            {
+               StripLastDirectory(filePath, temp);
                MakePathRelative(temp, project.topNode.path, temp);
                node.path = CopyUnixPath(temp);
             }
@@ -1003,6 +1096,7 @@ private:
          }
          files.Insert(after, node);
       }
+      delete exclusionInfo;
       return node;
    }
 
@@ -1127,34 +1221,26 @@ private:
       return result;
    }
 
-   void GenFileFlags(File f, Project project)
+   bool ContainsFilesWithExtension(char * extension)
    {
-      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)
       {
-         ProjectConfig config = node.config;
-         if(node.options && node.options.preprocessorDefinitions)
-            OutputListOption(f, "D", node.options.preprocessorDefinitions, inPlace, false);
-         if(config && config.options && config.options.preprocessorDefinitions)
-            OutputListOption(f, "D", config.options.preprocessorDefinitions, inPlace, false);
-         if(node.options && node.options.includeDirs)
-            OutputListOption(f, "I", node.options.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)
+      {
+         bool needed = false;
+         for(child : files)
+            if(child.ContainsFilesWithExtension(extension))
+               return true;
+      }
+      return false;
    }
 
-   void GenMakefileGetNameCollisionInfo(Map<String, NameCollisionInfo> namesInfo)
+   void GenMakefileGetNameCollisionInfo(Map<String, NameCollisionInfo> namesInfo, ProjectConfig prjConfig)
    {
       if(type == file)
       {
@@ -1191,19 +1277,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);
@@ -1233,7 +1321,7 @@ private:
          {
             if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
                   !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
-                  !strcmpi(extension, "ec") || !strcmpi(extension, "m"))
+                  !strcmpi(extension, "m"))
             {
                char modulePath[MAX_LOCATION];
 
@@ -1243,6 +1331,19 @@ private:
                items.Add(CopyString(s));
             }
          }
+         else if(printType == eCsources)
+         {
+            if(!strcmpi(extension, "ec"))
+            {
+               char modulePath[MAX_LOCATION];
+
+               ReplaceUnwantedMakeChars(modulePath, path);
+               ReplaceUnwantedMakeChars(moduleName, name);
+               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") ||
                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
                !strcmpi(extension, "m"))
@@ -1251,51 +1352,39 @@ private:
             {
                bool collision;
                NameCollisionInfo info;
+               count++;
                ReplaceSpaces(moduleName, name);
                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);
                items.Add(CopyString(s));
+               if(containsCXX && (!strcmpi(extension, "cpp") || !strcmpi(extension, "cc") || !strcmpi(extension, "cxx")))
+                  *containsCXX = true;
             }
          }
-         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));
-         }
          delete ts;
       }
       else if(files)
       {
          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<int, int> nodeCFlagsMapping, Map<int, int> nodeECFlagsMapping)
    {
+      int ifCount = 0;
+      Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(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)
@@ -1303,7 +1392,7 @@ private:
          char extension[MAX_EXTENSION];
          char modulePath[MAX_LOCATION];
          char moduleName[MAX_FILENAME];
-         
+
          GetExtension(name, extension);
          if(!strcmpi(extension, "ec"))
          {
@@ -1386,6 +1475,7 @@ private:
                if(!result)
                {
 #endif
+               OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
                   f.Printf("$(OBJ)%s.sym: %s%s.%s\n",
                      moduleName, modulePath, moduleName, extension);
 #if 0
@@ -1397,52 +1487,153 @@ private:
                modulePath, moduleName, extension, moduleName);
             */
 
-            f.Printf("\t$(ECP) $(CECFLAGS)");
-            if(ecflags)
+            f.Puts("\t$(ECP)");
+
+            f.Puts(" $(CECFLAGS)"); // tocheck: what of this? should this stuff be per-file customized?
+
+            GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
+            GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "CFLAGS", f);
+
+            f.Printf(" -c %s%s.%s -o $(OBJ)%s.sym\n\n",
+               modulePath, moduleName, extension, moduleName);
+            CloseRulesPlatformExclusionIfs(f, ifCount);
+         }
+      }
+      if(files)
+      {
+         bool needed = false;
+         if(ContainsFilesWithExtension("ec"))
+         {
+            for(child : files)
             {
-               if(memoryGuard)
-                  f.Printf(" -memguard");
-               if(strictNameSpaces)
-                  f.Printf(" -strictns");
+               if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
                {
-                  char * s = defaultNameSpace;
-                  if(s && s[0])
-                     f.Printf(" -defaultns %s", s);
+                  needed = true;
+                  break;
                }
             }
-            else
-               f.Printf(" $(ECFLAGS)");
-            f.Printf(" $(CFLAGS)");
-            GenFileFlags(f, project);
-            f.Printf(" -c %s%s.%s -o $(OBJ)%s.sym\n\n",
+         }
+         if(needed)
+         {
+            Map<Platform, bool> excludedPlatforms { };
+            for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
+            for(platform : platforms)
+            {
+               OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
+               for(child : files)
+               {
+                  if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+                     child.GenMakefilePrintSymbolRules(f, project, prjConfig, excludedPlatforms,
+                           nodeCFlagsMapping, nodeECFlagsMapping);
+               }
+            }
+            CloseRulesPlatformExclusionIfs(f, ifCount);
+            delete excludedPlatforms;
+         }
+      }
+      delete platforms;
+   }
+
+   void GenMakefilePrintPrepecsRules(File f, Project project,
+         ProjectConfig prjConfig, Map<Platform, bool> parentExcludedPlatforms,
+         Map<int, int> nodeCFlagsMapping, Map<int, int> nodeECFlagsMapping)
+   {
+      int ifCount = 0;
+      ProjectConfig config = GetMatchingNodeConfig(prjConfig);
+      Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(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"))
+         {
+            DualPipe dep;
+            char command[2048];
+
+            ReplaceSpaces(moduleName, name);
+            StripExtension(moduleName);
+
+            ReplaceSpaces(modulePath, path);
+            if(modulePath[0]) strcat(modulePath, SEPS);
+
+            OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
+            f.Printf("$(OBJ)%s$(EC): %s%s.%s\n",
+               moduleName, modulePath, moduleName, extension);
+            //$(CPP) -x c -E ../extras/gui/controls/DirectoriesBox.ec -o $(OBJ)DirectoriesBox$(EC)
+            /*f.Printf("\t$(CPP) %s%s.%s %s$(S)\n\n",
+               modulePath, moduleName, extension, moduleName);*/
+
+            f.Puts("\t$(CPP)");
+
+            //f.Puts(" $(CECFLAGS)");
+            //GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
+            GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "CFLAGS", f);
+
+            f.Printf(" -x c -E %s%s.%s -o $(OBJ)%s$(EC)\n\n",
                modulePath, moduleName, extension, moduleName);
+            CloseRulesPlatformExclusionIfs(f, ifCount);
          }
       }
       if(files)
       {
-         for(child : files)
+         bool needed = false;
+         if(ContainsFilesWithExtension("ec"))
          {
-            // 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)))
+               {
+                  needed = true;
+                  break;
+               }
+            }
+         }
+         if(needed)
+         {
+            Map<Platform, bool> excludedPlatforms { };
+            for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
+            for(platform : platforms)
+            {
+               OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
+               for(child : files)
+               {
+                  if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+                     child.GenMakefilePrintPrepecsRules(f, project, prjConfig, excludedPlatforms,
+                           nodeCFlagsMapping, nodeECFlagsMapping);
+               }
+            }
+            CloseRulesPlatformExclusionIfs(f, ifCount);
+            delete excludedPlatforms;
          }
       }
-      delete compiler;
+      delete platforms;
    }
 
-   void GenMakefilePrintCObjectRules(File f, Project project)
+   void GenMakefilePrintCObjectRules(File f, Project project,
+      ProjectConfig prjConfig, Map<Platform, bool> parentExcludedPlatforms,
+      Map<int, int> nodeCFlagsMapping, Map<int, int> nodeECFlagsMapping)
    {
+      int ifCount = 0;
+      ProjectConfig config = GetMatchingNodeConfig(prjConfig);
+      Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(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"))
          {
@@ -1474,7 +1665,7 @@ private:
                   strcat(command, item);
             }
 
-            for(item : project.config.includeDirs)
+            for(item : config.includeDirs)
             {
                strcat(command, " -I");
                if(strchr(item, ' '))
@@ -1486,7 +1677,7 @@ private:
                else
                   strcat(command, item);
             }
-            for(item : project.config.preprocessorDefs)
+            for(item : config.preprocessorDefs)
             {
                strcat(command, " -D");
                strcat(command, item);
@@ -1529,6 +1720,7 @@ private:
                      moduleName, modulePath, moduleName, extension);
                   */
 #endif
+            OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
                   f.Printf("$(OBJ)%s.c: %s%s.%s $(OBJ)%s.sym | $(SYMBOLS)\n",
                      moduleName, modulePath, moduleName, extension, moduleName);
 #if 0
@@ -1540,46 +1732,65 @@ private:
                modulePath, moduleName, extension, moduleName);
          */
 
-            f.Printf("\t$(ECC)");
-            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)");
-            GenFileFlags(f, project);
+            f.Puts("\t$(ECC)");
+
+            f.Puts(" $(CECFLAGS)"); // what of this? should this stuff be per-file customized?
+            GenMakePrintNodeFlagsVariable(this, nodeECFlagsMapping, "ECFLAGS", f);
+            GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "CFLAGS", f);
+            f.Puts(" $(FVISIBILITY)");
+
             f.Printf(" -c %s%s.%s -o $(OBJ)%s.c -symbols $(OBJ)\n\n",
                modulePath, moduleName, extension, moduleName);
+            CloseRulesPlatformExclusionIfs(f, ifCount);
          }
       }
       if(files)
       {
-         for(child : files)
+         bool needed = false;
+         if(ContainsFilesWithExtension("ec"))
          {
-            // 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)))
+               {
+                  needed = true;
+                  break;
+               }
+            }
+         }
+         if(needed)
+         {
+            Map<Platform, bool> excludedPlatforms { };
+            for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
+            for(platform : platforms)
+            {
+               OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
+               for(child : files)
+               {
+                  if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+                     child.GenMakefilePrintCObjectRules(f, project, prjConfig, excludedPlatforms,
+                           nodeCFlagsMapping, nodeECFlagsMapping);
+               }
+            }
+            CloseRulesPlatformExclusionIfs(f, ifCount);
+            delete excludedPlatforms;
          }
       }
-      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<int, int> nodeCFlagsMapping, Map<int, int> nodeECFlagsMapping)
    {
+      int ifCount = 0;
+      ProjectConfig config = GetMatchingNodeConfig(prjConfig);
+      Array<Platform> platforms = GetPlatformsArrayFromExcluisionInfo(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)
       {
@@ -1587,14 +1798,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"))
+         if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
+               !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
+               !strcmpi(extension, "m") || !strcmpi(extension, "ec"))
          {
             DualPipe dep;
             char command[2048];
@@ -1611,9 +1822,10 @@ private:
 
             // *** 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);
@@ -1634,7 +1846,7 @@ private:
                      strcat(command, item);
                }
 
-               for(item : project.config.includeDirs)
+               for(item : config.includeDirs)
                {
                   strcat(command, " -I");
                   if(strchr(item, ' '))
@@ -1646,7 +1858,7 @@ private:
                   else
                      strcat(command, item);
                }
-               for(item : project.config.preprocessorDefs)
+               for(item : config.preprocessorDefs)
                {
                   strcat(command, " -D");
                   strcat(command, item);
@@ -1686,6 +1898,8 @@ private:
                   if(!result)
                   {
 #endif
+            OpenRulesPlatformExclusionIfs(f, &ifCount, platforms[0], parentExcludedPlatforms, null);
+
                      /*if(!strcmpi(extension, "ec"))
                         f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
                      else*/
@@ -1696,29 +1910,52 @@ private:
                }
 #endif
             }
-            f.Printf("\t$(CC) $(CFLAGS)");
-            GenFileFlags(f, project);
+            f.Printf("\t$(%s)", (!strcmpi(extension, "cc") || !strcmpi(extension, "cpp") || !strcmpi(extension, "cxx")) ? "CXX" : "CC");
+
+            GenMakePrintNodeFlagsVariable(this, nodeCFlagsMapping, "CFLAGS", f);
+
             if(!strcmpi(extension, "ec"))
                f.Printf(" $(FVISIBILITY) -c $(OBJ)%s.c -o $(OBJ)%s.o\n\n", moduleName, moduleName);
             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 : "");
+            CloseRulesPlatformExclusionIfs(f, ifCount);
          }
       }
       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)
+         {
+            Map<Platform, bool> excludedPlatforms { };
+            for(mn : parentExcludedPlatforms) if(mn) excludedPlatforms[&mn] = true;
+            for(platform : platforms)
+            {
+               OpenRulesPlatformExclusionIfs(f, &ifCount, platform, parentExcludedPlatforms, excludedPlatforms);
+               for(child : files)
+               {
+                  if(child.type != resources && (child.type == folder || !child.GetIsExcluded(prjConfig)))
+                     child.GenMakefilePrintObjectRules(f, project, namesInfo, prjConfig, excludedPlatforms,
+                           nodeCFlagsMapping, nodeECFlagsMapping);
+               }
+            }
+            CloseRulesPlatformExclusionIfs(f, ifCount);
+            delete excludedPlatforms;
          }
       }
-      delete compiler;
+      delete platforms;
    }
 
-   void GenMakefileAddResources(File f, String resourcesPath)
+   void GenMakefileAddResources(File f, String resourcesPath, ProjectConfig prjConfig)
    {
       int count = 0;
       if(files)
@@ -1732,10 +1969,10 @@ 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];
@@ -1745,7 +1982,7 @@ private:
 
                // $(EAR) aw%s --- /*quiet ? "q" : */""
                if(count == 0)
-                  f.Printf("\t%s$(EAR) aw $(TARGET)", ts.a);
+                  f.Printf("\t%s$(EAR) $(EARFLAGS) $(TARGET)", ts.a);
 
                tempPath[0] = '\0';
                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
@@ -1764,7 +2001,7 @@ private:
                   quotes = "\"";
                else
                   quotes = "";
-               f.Printf(" %s%s%s%s", quotes, useRes ? "$(RES)" : "", resPath, quotes);
+               f.Printf(" %s%s%s%s", quotes, useRes ? "$(RES)" : "", tempPath, quotes);
                count++;
             }
             if(count == 10 || (count > 0 && (ts || !child.next)))
@@ -1795,10 +2032,851 @@ private:
          for(child : files)
          {
             if(child.type == folder)
-               child.GenMakefileAddResources(f, resourcesPath);
+               child.GenMakefileAddResources(f, resourcesPath, prjConfig);
          }
       }
    }
+
+   void GenMakeCollectAssignNodeFlags(ProjectConfig prjConfig, bool prjWithEcFiles,
+         Map<String, int> cflagsVariations, Map<int, int> nodeCFlagsMapping,
+         Map<String, int> ecflagsVariations, Map<int, 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)
+            {
+               if(!isGreater)
+               {
+                  // absolutely common stuff outside of platform only, stuff that can't be changed by platform
+                 // This would normally go in crossplatform.mk (or compiler.cf if compiler-specific)
+                  // cflags.concatf(" \\\n\t $(if $(DEBIAN_PACKAGE),$(CPPFLAGS),) $(if $(DEBUG), -D_DEBUG,)");
+               }
+
+               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)
+                  cflags.concatf(isGreater ? "%s" : " \\\n\t%s", (String)s);
+               delete s;
+               s = { };
+               GenECFlagsFromProjectOptions(platformsCommonOptions, prjWithEcFiles, s);
+               if(s.count > 1)
+                  ecflags.concatf(" \\\n\t%s", (String)s);
+               delete s;
+
+               if(isGreater)
+               {
+                  cflags.concatf(" \\\n\t");
+                  DynStringPrintNodeFlagsVariable(parent, nodeCFlagsMapping, "CFLAGS", cflags);
+               }
+            }
+
+            additionsByPlatformOptions.Free();
+            delete additionsByPlatformOptions;
+
+            // output
+            {
+               if(isEqual)
+               {
+                  nodeCFlagsMapping[(int)this] = nodeCFlagsMapping[(int)parent];
+                  nodeECFlagsMapping[(int)this] = nodeECFlagsMapping[(int)parent];
+               }
+               else
+               {
+                  String s;
+                  int variationNum;
+
+                  variationNum = 1;
+                  if((s = cflags) && s[0] && !(variationNum = cflagsVariations[s]))
+                     cflagsVariations[s] = variationNum = cflagsVariations.count;
+                  nodeCFlagsMapping[(int)this] = variationNum;
+
+                  variationNum = 1;
+                  if((s = ecflags) && s[0] && !(variationNum = ecflagsVariations[s]))
+                     ecflagsVariations[s] = variationNum = ecflagsVariations.count;
+                  nodeECFlagsMapping[(int)this] = variationNum;
+               }
+            }
+
+            delete cflags;
+            delete ecflags;
+         }
+         else
+         {
+            // output
+            {
+               nodeCFlagsMapping[(int)this] = nodeCFlagsMapping[(int)parent];
+               nodeECFlagsMapping[(int)this] = nodeECFlagsMapping[(int)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> GetPlatformsArrayFromExcluisionInfo(ProjectConfig prjConfig)
+   {
+      Array<Platform> platforms { };
+      Map<Platform, SetBool> exclusionInfo { };
+      CollectExclusionInfo(exclusionInfo, prjConfig);
+      if(exclusionInfo[unknown] == true && exclusionInfo.count > 1)
+         for(mn : exclusionInfo; mn == false)
+            platforms.Add(&mn);
+      else
+         platforms.Add(unknown);
+      delete exclusionInfo;
+      return platforms;
+   }
+}
+
+// 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 e;
+   int o;
+   int priority = 0;
+   int includeDirsOption = OPTION(includeDirs);
+   ProjectNode n;
+   char * platformName = platform ? platform.OnGetString(0,0,0) : null;
+
+   Array<bool> optionConfigXplatformSet   { size = OPTION(postbuildCommands) };
+   Array<bool> optionDone                 { size = OPTION(postbuildCommands) };
+   Array<Array<String>> optionTempStrings { size = OPTION(postbuildCommands) };
+
+   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) {
+         String string = *(String*)((byte *)options + option);
+         return string && string[0];
+      }
+      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, "%04d\n", priority * 100 + 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;
+      }
+   };
+   GenericOptionTools<BuildBitDepth>        utilBuildBitDepth {
+      bool OptionCheck(ProjectOptions options, int option) {
+         BuildBitDepth value = *(BuildBitDepth*)((byte *)options + option);
+         return value && value != all;
+      }
+      void LoadOption(ProjectOptions options, int option, int priority, Array<Array<String>> optionTempStrings, ProjectOptions output) {
+         BuildBitDepth value = options ? *(BuildBitDepth*)((byte *)options + option) : (BuildBitDepth)0;
+         *(BuildBitDepth*)((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;
+
+   ot[OPTION(buildBitDepth)] =           utilBuildBitDepth;
+
+   for(n = node; n; n = n.parent)
+   {
+      ProjectConfig nodeConfig = null;
+      if(n.parent)
+         priority++;
+      else
+         priority = 99;
+      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 : 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 : 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 : 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 utilBuildBitDepth;
+
+   delete ot;
+
+   return output;
+}
+
+static void CollectPlatformsCommonOptions(Map<Platform, ProjectOptions> byPlatformOptions, ProjectOptions * platformsCommonOptions)
+{
+   char * s;
+   int i;
+   ProjectOptions first;
+   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.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.buildBitDepth && options.buildBitDepth != commonOptions.buildBitDepth)
+            commonOptions.buildBitDepth = all;
+
+         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.buildBitDepth && options.buildBitDepth == commonOptions.buildBitDepth)
+         options.buildBitDepth = all;
+
+      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
+            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)
+      {
+         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<int, int> nodeFlagsMapping, String variableName, File f)
+{
+   int customFlags;
+   customFlags = nodeFlagsMapping[(int)node];
+   if(customFlags > 1)
+      f.Printf(" $(CUSTOM%d_%s)", customFlags-1, variableName);
+   else
+      f.Printf(" $(%s)", variableName);
+}
+
+static void DynStringPrintNodeFlagsVariable(ProjectNode node, Map<int, int> nodeFlagsMapping, String variableName, DynamicString s)
+{
+   int customFlags;
+   customFlags = nodeFlagsMapping[(int)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.concatf(" $(if $(DEBUG),");
+               s.concatf(" -g");
+               s.concatf(",");
+            }
+            switch(options.optimization)
+            {
+               case speed: s.concatf(" -O2"); break;
+               case size: s.concatf(" -Os"); break;
+            }
+            if(options.fastMath == true)
+               s.concatf(" -ffast-math");
+            if(options.debug == true)
+               s.concatf(" -g");
+            if(options.debug != true)
+               s.concatf(")");
+         }
+         else if(commonOptions)
+            s.concatf(" $(if $(DEBUG),-g)");
+         if(options.buildBitDepth || (commonOptions && prjWithEcFiles))
+            s.concatf(" %s", (!options || !options.buildBitDepth || options.buildBitDepth == bits32) ? "$(FORCE_32_BIT)" : "$(FORCE_64_BIT)");
+         if(commonOptions)
+            s.concatf(" $(FPIC)");
+      }
+      switch(options.warnings)
+      {
+         case all: s.concatf(" -Wall"); break;
+         case none: s.concatf(" -w"); break;
+      }
+      if(options.profile)
+         s.concatf(" -pg");
+   }
+
+   if(options && options.preprocessorDefinitions)
+      ListOptionToDynamicString("D", options.preprocessorDefinitions, false, lineEach, "\t\t\t", false, s);
+   if(options && options.includeDirs)
+      ListOptionToDynamicString("I", options.includeDirs, true, lineEach, "\t\t\t", true, s);
+}
+
+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(char * option, Array<String> list, bool prioritize,
+      ListOutputMethod method, String newLineStart, bool noSpace, DynamicString s)
+{
+   if(list.count)
+   {
+      if(method == newLine)
+         s.concatf(" \\\n%s", 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(method == lineEach)
+               s.concatf(" \\\n%s", newLineStart);
+            s.concatf(" -%s", option);
+            if(noSpace)
+               StringNoSpaceToDynamicString(s, start ? start+1 : mn.key);
+            else
+               s.concat(start ? start+1 : mn.key);
+         }
+         delete sortedList;
+      }
+      else
+      {
+         for(item : list)
+         {
+            if(method == lineEach)
+               s.concatf(" \\\n%s", newLineStart);
+            s.concatf(" -%s", option);
+            if(noSpace)
+               StringNoSpaceToDynamicString(s, item);
+            else
+               s.concat(item);
+         }
+      }
+   }
+}
+
+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
@@ -1825,3 +2903,28 @@ class NameCollisionInfo
      return colliding;
    }
 }
+
+static inline void OpenRulesPlatformExclusionIfs(File f, int * ifCount, Platform platform,
+      Map<Platform, bool> parentExcludedPlatforms, Map<Platform, bool> excludedPlatforms)
+{
+   if(platform != unknown && !parentExcludedPlatforms[platform])
+   {
+      if(*ifCount)             // we really need a if defined(a) || defined(b) here
+         f.Puts("else\n");     // instead of repeating the rules for each platform
+      (*ifCount)++;            // hmm... what?
+      f.Printf("ifdef %s\n\n", PlatformToMakefileTargetVariable(platform));
+      if(excludedPlatforms)
+         excludedPlatforms[platform] = true;
+   }
+}
+
+static inline void CloseRulesPlatformExclusionIfs(File f, int ifCount)
+{
+   if(ifCount)
+   {
+      int c;
+      for(c = 0; c < ifCount; c++)
+         f.Puts("endif\n");
+      f.Puts("\n");
+   }
+}