sdk/Build System: Took out extra inclusion of LDFLAGS; Moved $(CPPFLAGS) and -D_DEBUG...
[sdk] / ide / src / project / Project.ec
old mode 100644 (file)
new mode 100755 (executable)
index ae11e04..9778dbc
@@ -4,8 +4,12 @@ public import static "ecere"
 public import "ecere"
 #endif
 
+import "DynamicString"
+
 #ifndef MAKEFILE_GENERATOR
 import "ide"
+// We should have the .sln/.vcproj generation even on other platforms
+// e.g. detect from an environment variable pointing to a Windows drive
 #ifdef __WIN32__
 import "vsSupport"
 #endif
@@ -15,8 +19,6 @@ import "ProjectConfig"
 import "ProjectNode"
 import "IDESettings"
 
-import "PathBox"
-
 default:
 
 static void DummyFunction()
@@ -44,7 +46,6 @@ IDESettingsContainer settingsContainer
 #ifndef MAKEFILE_GENERATOR
       globalSettingsDialog.ideSettings = settings;
       ide.UpdateRecentMenus();
-      // ide.UpdateMakefiles(); -- can't really regenerate on Load since all recent menus changes will happen
 #endif
    }
 };
@@ -374,11 +375,13 @@ define PEEK_RESOLUTION = (18.2 * 10);
 
 static byte epjSignature[] = { 'E', 'P', 'J', 0x04, 0x01, 0x12, 0x03, 0x12 };
 
-enum GenMakefilePrintTypes { objects, cObjects, symbols, imports, sources, resources };
+enum GenMakefilePrintTypes { objects, cObjects, symbols, imports, sources, resources, eCsources };
 
 define WorkspaceExtension = "ews";
 define ProjectExtension = "epj";
 
+define stringInFileIncludedFrom = "In file included from ";
+
 void ReplaceSpaces(char * output, char * source)
 {
    int c, dc;
@@ -399,6 +402,28 @@ void ReplaceSpaces(char * output, char * source)
    output[dc] = '\0';
 }
 
+void ReplaceUnwantedMakeChars(char * output, char * source)
+{
+   int c, dc;
+   char ch, pch = 0;
+
+   for(c = 0, dc = 0; (ch = source[c]); c++, dc++)
+   {
+      if(pch != '$')
+      {
+         if(ch == '(' || ch == ')') output[dc++] = '\\';
+         pch = ch;
+      }
+      else if(ch == ')')
+         pch = 0;
+      if(ch == ' ')
+         output[dc] = 127;
+      else
+         output[dc] = ch;
+   }
+   output[dc] = '\0';
+}
+
 static void OutputNoSpace(File f, char * source)
 {
    char * output = new char[strlen(source)+1024];
@@ -409,11 +434,11 @@ static void OutputNoSpace(File f, char * source)
 
 enum ListOutputMethod { inPlace, newLine, lineEach };
 
-int OutputFileList(File f, char * name, Array<String> list, Map<String, int> varStringLenDiffs)
+int OutputFileList(File f, char * name, Array<String> list, Map<String, int> varStringLenDiffs, char * prefix)
 {
    int numOfBreaks = 0;
    const int breakListLength = 1536;
-   const int breakLineLength = 78;
+   const int breakLineLength = 78; // TODO: turn this into an option.
 
    int c, len, itemCount = 0;
    Array<int> breaks { };
@@ -448,13 +473,13 @@ int OutputFileList(File f, char * name, Array<String> list, Map<String, int> var
 
    if(numOfBreaks > 1)
    {
-      f.Printf("%s =", name);
+      f.Printf("%s =%s%s", name, prefix ? " " : "", prefix ? prefix : "");
       for(c=0; c<numOfBreaks; c++)
          f.Printf(" $(%s%d)", name, c+1);
-      f.Printf("\n");
+      f.Puts("\n");
    }
    else
-      f.Printf("%s =", name);
+      f.Printf("%s =%s%s", name, prefix ? " " : "", prefix ? prefix : "");
 
    if(numOfBreaks)
    {
@@ -469,27 +494,32 @@ int OutputFileList(File f, char * name, Array<String> list, Map<String, int> var
          itemCount = breaks[c];
          for(n=offset; n<offset+itemCount; n++)
          {
-            int itemLen = strlen(list[n]);
-            if(len > 3 && len + itemLen > breakLineLength)
+            if(false) // TODO: turn this into an option.
             {
-               f.Printf(" \\\n\t%s", list[n]);
-               len = 3;
+               int itemLen = strlen(list[n]);
+               if(len > 3 && len + itemLen > breakLineLength)
+               {
+                  f.Printf(" \\\n\t%s", list[n]);
+                  len = 3;
+               }
+               else
+               {
+                  len += itemLen;
+                  f.Printf(" %s", list[n]);
+               }
             }
             else
-            {
-               len += itemLen;
-               f.Printf(" %s", list[n]);
-            }
+               f.Printf(" \\\n\t%s", list[n]);
          }
          offset += itemCount;
-         f.Printf("\n");
+         f.Puts("\n");
       }
       list.Free();
       list.count = 0;
    }
    else
-      f.Printf("\n");
-   f.Printf("\n");
+      f.Puts("\n");
+   f.Puts("\n");
    delete breaks;
    return numOfBreaks;
 }
@@ -511,11 +541,11 @@ void OutputListOption(File f, char * option, Array<String> list, ListOutputMetho
    if(list.count)
    {
       if(method == newLine)
-         f.Printf(" \\\n\t");
+         f.Puts(" \\\n\t");
       for(item : list)
       {
          if(method == lineEach)
-            f.Printf(" \\\n\t");
+            f.Puts(" \\\n\t");
          f.Printf(" -%s", option);
          if(noSpace)
             OutputNoSpace(f, item);
@@ -525,6 +555,14 @@ void OutputListOption(File f, char * option, Array<String> list, ListOutputMetho
    }
 }
 
+void StringNoSpaceToDynamicString(DynamicString s, char * path)
+{
+   char * output = new char[strlen(path)+1024];
+   ReplaceSpaces(output, path);
+   s.concat(output);
+   delete output;
+}
+
 static void OutputLibraries(File f, Array<String> libraries)
 {
    for(item : libraries)
@@ -532,9 +570,10 @@ static void OutputLibraries(File f, Array<String> libraries)
       char ext[MAX_EXTENSION];
       char temp[MAX_LOCATION];
       char * s = item;
+      bool usedFunction = false;
       GetExtension(item, ext);
       if(!strcmp(ext, "o") || !strcmp(ext, "a"))
-         f.Printf(" ");
+         f.Puts(" ");
       else
       {
          if(!strcmp(ext, "so") || !strcmp(ext, "dylib"))
@@ -546,9 +585,12 @@ static void OutputLibraries(File f, Array<String> libraries)
             StripExtension(temp);
             s = temp;
          } 
-         f.Printf(" -l");
+         f.Puts(" \\\n\t$(call _L,");
+         usedFunction = true;
       }
       OutputNoSpace(f, s);
+      if(usedFunction)
+         f.Puts(")");
    }
 }
 
@@ -593,6 +635,9 @@ define localProfile = config && config.options && config.options.profile ?
 define localOptimization = config && config.options && config.options.optimization ?
             config.options.optimization : options && options.optimization ?
             options.optimization : OptimizationStrategy::none;
+define localFastMath = config && config.options && config.options.fastMath ?
+            config.options.fastMath : options && options.fastMath ?
+            options.fastMath : SetBool::unset;
 define localDefaultNameSpace = config && config.options && config.options.defaultNameSpace ?
             config.options.defaultNameSpace : options && options.defaultNameSpace ?
             options.defaultNameSpace : null;
@@ -622,9 +667,33 @@ define localCompress = config && config.options && config.options.compress ?
             config.options.compress : options && options.compress ?
             options.compress : SetBool::unset;
 
-char * PlatformToMakefileVariable(Platform platform)
+define platformTargetType =
+         configPOs && configPOs.options && configPOs.options.targetType && configPOs.options.targetType != localTargetType ?
+               configPOs.options.targetType :
+         projectPOs && projectPOs.options && projectPOs.options.targetType && projectPOs.options.targetType != localTargetType ?
+               projectPOs.options.targetType : TargetTypes::unset;
+
+
+char * PlatformToMakefileTargetVariable(Platform platform)
+{
+   return platform == win32 ? "WINDOWS_TARGET" :
+          platform == tux   ? "LINUX_TARGET"   :
+          platform == apple ? "OSX_TARGET"     :
+                              "ERROR_BAD_TARGET";
+}
+
+char * TargetTypeToMakefileVariable(TargetTypes targetType)
+{
+   return targetType == executable    ? "executable" :
+          targetType == sharedLibrary ? "sharedlib"  :
+          targetType == staticLibrary ? "staticlib"  :
+                                        "unknown";
+}
+
+// Move this to ProjectConfig? null vs Common to consider...
+char * GetConfigName(ProjectConfig config)
 {
-   return platform == win32 ? "WINDOWS" : platform == tux ? "LINUX" : platform == apple ? "OSX"/*"APPLE"*/ : platform;
+   return config ? config.name : "Common";
 }
 
 class Project : struct
@@ -635,18 +704,67 @@ class Project : struct
 public:
    float version;
    String moduleName;
-   String description;
-   String license;
 
-   ProjectOptions options;
-   Array<PlatformOptions> platforms;
+   property ProjectOptions options { get { return options; } set { options = value; } isset { return options && !options.isEmpty; } }
+   property Array<PlatformOptions> platforms
+   {
+      get { return platforms; }
+      set
+      {
+         if(platforms) { platforms.Free(); delete platforms; }
+         if(value)
+         {
+            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;
+         }
+      }
+      isset
+      {
+         if(platforms)
+         {
+            for(p : platforms)
+            {
+               if(p.options && !p.options.isEmpty)
+                  return true;
+            }
+         }
+         return false;
+      }
+   }
    List<ProjectConfig> configurations;
    LinkList<ProjectNode> files;
    String resourcesPath;
    LinkList<ProjectNode> resources;
 
+   property char * description
+   {
+      set { delete description; if(value && value[0]) description = CopyString(value); }
+      get { return description ? description : ""; }
+      isset { return description != null && description[0]; }
+   }
+
+   property char * license
+   {
+      set { delete license; if(value && value[0]) license = CopyString(value); }
+      get { return license ? license : ""; }
+      isset { return license != null && license[0]; }
+   }
+
+   property char * compilerConfigsDir
+   {
+      set { delete compilerConfigsDir; if(value && value[0]) compilerConfigsDir = CopyString(value); }
+      get { return compilerConfigsDir ? compilerConfigsDir : ""; }
+      isset { return compilerConfigsDir && compilerConfigsDir[0]; }
+   }
+
 private:
    // topNode.name holds the file name (.epj)
+   ProjectOptions options;
+   Array<PlatformOptions> platforms;
    ProjectNode topNode { type = project, icon = epjFile, files = LinkList<ProjectNode>{ }, project = this };
    ProjectNode resNode;
 
@@ -656,12 +774,82 @@ private:
    // It should NOT be edited, saved or loaded anywhere
    String name;
 
-   ~Project()
+   String description;
+   String license;
+   String compilerConfigsDir;
+#ifndef MAKEFILE_GENERATOR
+   FileMonitor fileMonitor
    {
-      topNode.configurations = null;
-      topNode.platforms = null;
-      topNode.options = null;
+      this, FileChange { modified = true };
+      bool OnFileNotify(FileChange action, char * param)
+      {
+         fileMonitor.StopMonitoring();
+         if(OnProjectModified(action, param))
+            fileMonitor.StartMonitoring();
+         return true;
+      }
+   };
+
+   bool StartMonitoring()
+   {
+      fileMonitor.fileName = filePath;
+      fileMonitor.StartMonitoring();
+      return true;
+   }
+
+   bool StopMonitoring()
+   {
+      fileMonitor.StopMonitoring();
+      return true;
+   }
+
+   bool OnProjectModified(FileChange fileChange, char * param)
+   {
+      char temp[4096];
+      sprintf(temp, $"The project %s was modified by another application.\n"
+            "Would you like to reload it and lose your changes?", name);
+      if(MessageBox { type = yesNo, master = ide,
+            text = $"Project has been modified", contents = temp }.Modal() == yes)
+      {
+         Project project = LoadProject(filePath, config.name);
+         if(project)
+         {
+            ProjectView projectView = ide.projectView;
+            DataRow prev = topNode.row ? topNode.row.previous : null;
+            FileMonitor fm = fileMonitor;
+
+            if(projectView) projectView.DeleteNode(topNode);
+
+            *this = *project;
+            delete fileMonitor;
+            fileMonitor = fm;
+            topNode.project = this;
+
+            if(projectView)
+            {
+               CompilerConfig compiler = ideSettings.GetCompilerConfig(projectView.workspace.compiler);
+               projectView.AddNode(topNode, null);
+               topNode.row.Move(prev);
+
+               projectView.ShowOutputBuildLog(true);
+               projectView.DisplayCompiler(compiler, false);
+               projectView.ProjectUpdateMakefileForAllConfigs(this);
+               delete compiler;
+            }
+            eSystem_Delete(project);
+         }
+         return true;
+      }
+      return true;
+   }
+
+#endif
 
+   // This frees contents without freeing the instance
+   // For use from destructor and for file monitor reloading
+   // (To work around JSON loader (LoadProject) always returning a new instance)
+   void Free()
+   {
       if(platforms) { platforms.Free(); delete platforms; }
       if(configurations) { configurations.Free(); delete configurations; }
       if(files) { files.Free(); delete files; }
@@ -671,13 +859,22 @@ private:
 
       delete description;
       delete license;
+      delete compilerConfigsDir;
       delete moduleName;
       delete filePath;
       delete topNode;
       delete name;
    }
 
-   property char * configName { get { return config ? config.name : "Common"; } }
+   ~Project()
+   {
+      /* // THIS IS NOW AUTOMATED WITH A project CHECK IN ProjectNode
+      topNode.configurations = null;
+      topNode.platforms = null;
+      topNode.options = null;
+      */
+      Free();
+   }
 
    property ProjectConfig config
    {
@@ -685,7 +882,7 @@ private:
       {
          config = value;
          delete topNode.info;
-         topNode.info = CopyString(configName);
+         topNode.info = CopyString(GetConfigName(config));
       }
    }
    property char * filePath
@@ -710,175 +907,153 @@ private:
       }
    }
 
-   property TargetTypes targetType
+   TargetTypes GetTargetType(ProjectConfig config)
    {
-      get
-      {
-         // TODO: Implement platform specific options?
-         TargetTypes targetType = localTargetType;
-         return targetType;
-      }
+      TargetTypes targetType = localTargetType;
+      return targetType;
    }
 
-   property char * objDirExpression
+   bool GetTargetTypeIsSetByPlatform(ProjectConfig config)
    {
-      get
+      Platform platform;
+      for(platform = (Platform)1; platform < Platform::enumSize; platform++)
       {
-         // TODO: Support platform options
-         char * expression = localObjectsDirectory;
-         if(!expression)
-            expression = settingsObjectsDirectory;
-         return expression;
+         PlatformOptions projectPOs, configPOs;
+         MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
+         if(platformTargetType)
+            return true;
       }
+      return false;
    }
-   property DirExpression objDir
+
+
+   char * GetObjDirExpression(ProjectConfig config)
    {
-      get
-      {
-         char * expression = objDirExpression;
-         DirExpression objDir { type = intermediateObjectsDir };
-         objDir.Evaluate(expression, this);
-         return objDir;
-      }
+      // TODO: Support platform options
+      char * expression = localObjectsDirectory;
+      if(!expression)
+         expression = settingsObjectsDirectory;
+      return expression;
    }
-   property char * targetDirExpression
+
+   DirExpression GetObjDir(CompilerConfig compiler, ProjectConfig config)
    {
-      get
-      {
-         // TODO: Support platform options
-         char * expression = localTargetDirectory;
-         if(!expression)
-            expression = settingsTargetDirectory;
-         return expression;
-      }
+      char * expression = GetObjDirExpression(config);
+      DirExpression objDir { type = intermediateObjectsDir };
+      objDir.Evaluate(expression, this, compiler, config);
+      return objDir;
    }
-   property DirExpression targetDir
+
+   char * GetTargetDirExpression(ProjectConfig config)
    {
-      get
-      {
-         char * expression = targetDirExpression;
-         DirExpression targetDir { type = DirExpressionType::targetDir /*intermediateObjectsDir*/};
-         targetDir.Evaluate(expression, this);
-         return targetDir;
-      }
+      // TODO: Support platform options
+      char * expression = localTargetDirectory;
+      if(!expression)
+         expression = settingsTargetDirectory;
+      return expression;
    }
 
-   property WarningsOption warnings
+   DirExpression GetTargetDir(CompilerConfig compiler, ProjectConfig config)
    {
-      get
-      {
-         WarningsOption warnings = localWarnings;
-         return warnings;
-      }
+      char * expression = GetTargetDirExpression(config);
+      DirExpression targetDir { type = DirExpressionType::targetDir /*intermediateObjectsDir*/};
+      targetDir.Evaluate(expression, this, compiler, config);
+      return targetDir;
    }
-   property bool debug
+
+   WarningsOption GetWarnings(ProjectConfig config)
    {
-      get
-      {
-         SetBool debug = localDebug;
-         return debug == true;
-      }
+      WarningsOption warnings = localWarnings;
+      return warnings;
    }
-   property bool memoryGuard
+
+   bool GetDebug(ProjectConfig config)
    {
-      get
-      {
-         SetBool memoryGuard = localMemoryGuard;
-         return memoryGuard == true;
-      }
+      SetBool debug = localDebug;
+      return debug == true;
    }
-   property bool noLineNumbers
+
+   bool GetMemoryGuard(ProjectConfig config)
    {
-      get
-      {
-         SetBool noLineNumbers = localNoLineNumbers;
-         return noLineNumbers == true;
-      }
+      SetBool memoryGuard = localMemoryGuard;
+      return memoryGuard == true;
    }
-   property bool profile
+
+   bool GetNoLineNumbers(ProjectConfig config)
    {
-      get
-      {
-         SetBool profile = localProfile;
-         return profile == true;
-      }
+      SetBool noLineNumbers = localNoLineNumbers;
+      return noLineNumbers == true;
+   }
+
+   bool GetProfile(ProjectConfig config)
+   {
+      SetBool profile = localProfile;
+      return profile == true;
    }
-   property OptimizationStrategy optimization
+
+   OptimizationStrategy GetOptimization(ProjectConfig config)
    {
-      get
-      {
-         OptimizationStrategy optimization = localOptimization;
-         return optimization;
-      }
+      OptimizationStrategy optimization = localOptimization;
+      return optimization;
    }
-   property String defaultNameSpace
+
+   bool GetFastMath(ProjectConfig config)
    {
-      get
-      {
-         String defaultNameSpace = localDefaultNameSpace;
-         return defaultNameSpace;
-      }
+      SetBool fastMath = localFastMath;
+      return fastMath == true;
    }
-   property bool strictNameSpaces
+
+   String GetDefaultNameSpace(ProjectConfig config)
    {
-      get
-      {
-         SetBool strictNameSpaces = localStrictNameSpaces;
-         return strictNameSpaces == true;
-      }
+      String defaultNameSpace = localDefaultNameSpace;
+      return defaultNameSpace;
    }
-   property String targetFileName
+
+   bool GetStrictNameSpaces(ProjectConfig config)
    {
-      get
-      {
-         String targetFileName = localTargetFileName;
-         return targetFileName;
-      }
+      SetBool strictNameSpaces = localStrictNameSpaces;
+      return strictNameSpaces == true;
+   }
+
+   String GetTargetFileName(ProjectConfig config)
+   {
+      String targetFileName = localTargetFileName;
+      return targetFileName;
    }
+
    //String targetDirectory;
    //String objectsDirectory;
-   property bool console
+   bool GetConsole(ProjectConfig config)
    {
-      get
-      {
-         SetBool console = localConsole;
-         return console == true;
-      }
+      SetBool console = localConsole;
+      return console == true;
    }
-   property bool compress
+
+   bool GetCompress(ProjectConfig config)
    {
-      get
-      {
-         SetBool compress = localCompress;
-         return compress == true;
-      }
+      SetBool compress = localCompress;
+      return compress == true;
    }
    //SetBool excludeFromBuild;
 
-   property bool configIsInActiveDebugSession
+   bool GetConfigIsInActiveDebugSession(ProjectConfig config)
    {
-      get
-      {
 #ifndef MAKEFILE_GENERATOR
-         return ide.project == this  && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isActive;
+      return ide.project == this && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isActive;
 #endif
-      }
    }
 
-   property bool configIsInDebugSession
+   bool GetConfigIsInDebugSession(ProjectConfig config)
    {
-      get
-      {
 #ifndef MAKEFILE_GENERATOR
-         return ide.project == this  && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isPrepared;
+      return ide.project == this && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isPrepared;
 #endif
-      }
    }
 
-   void SetPath(bool projectsDirs)
+   void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config)
    {
 #ifndef MAKEFILE_GENERATOR
-      ide.SetPath(projectsDirs);
+      ide.SetPath(projectsDirs, compiler, config);
 #endif
    }
 
@@ -912,6 +1087,7 @@ private:
    }
 #endif
 
+   // This method is only called from Debugger, should be moved to Debugger class?
 #ifndef MAKEFILE_GENERATOR
    bool GetRelativePath(char * filePath, char * relativePath)
    {
@@ -925,15 +1101,53 @@ private:
          PathCatSlash(relativePath, node.name);
          return true;
       }
+      else
+      {
+         // Tweak for automatically resolving symbol loader modules
+         char * sl = strstr(moduleName, ".main.ec");
+         if(sl && (*sl = 0, !strcmpi(moduleName, name)))
+         {
+            char objDir[MAX_LOCATION];
+            DirExpression objDirExp;
+            CompilerConfig compiler = ide.debugger.currentCompiler;
+            ProjectConfig config = ide.debugger.prjConfig;
+            // This is not perfect, as multiple source files exist for the symbol loader module...
+            // We try to set it in the debug config object directory.
+            if(!compiler || !config)
+            {
+               // If we're not currently debugging, set a breakpoint in the active compiler/config
+               compiler = GetCompilerConfig();
+               config = this.config;
+               // If the current config is not debuggable, set it in the first debuggable config found
+               if(config && !config.options.debug)
+               {
+                  for(c : configurations; c.options.debug)
+                  {
+                     config = c;
+                     break;
+                  }
+               }
+            }
+            objDirExp = GetObjDir(compiler, config);
+            strcpy(objDir, objDirExp.dir);
+            delete objDirExp;
+            ChangeCh(objDir, '\\', '/'); // TODO: this is a hack, paths should never include win32 path seperators - fix this in ProjectSettings and ProjectLoad instead
+            ReplaceSpaces(objDir, objDir);
+            strcpy(relativePath, objDir);
+            *sl = '.';
+            PathCatSlash(relativePath, moduleName);
+            return true;
+         }
+      }
       // WARNING: On failure, relative path is uninitialized
       return false;   
    }
 #endif
 
-   void CatTargetFileName(char * string)
+   void CatTargetFileName(char * string, CompilerConfig compiler, ProjectConfig config)
    {
-      CompilerConfig compiler = GetCompilerConfig();
-
+      TargetTypes targetType = GetTargetType(config);
+      String targetFileName = GetTargetFileName(config);
       if(targetType == staticLibrary)
       {
          PathCatSlash(string, "lib");
@@ -965,19 +1179,78 @@ private:
             strcat(string, ".a");
             break;
       }
-      delete compiler;
    }
 
-   void CatMakeFileName(char * string)
+   bool GetProjectCompilerConfigsDir(char * cfDir, bool replaceSpaces, bool makeRelative)
+   {
+      bool result = false;
+      char temp[MAX_LOCATION];
+      strcpy(cfDir, topNode.path);
+      if(compilerConfigsDir && compilerConfigsDir[0])
+      {
+         PathCatSlash(cfDir, compilerConfigsDir);
+         result = true;
+      }
+      if(makeRelative)
+      {
+         strcpy(temp, cfDir);
+         // Using a relative path makes it less likely to run into spaces issues
+         // Even with escaped spaces, there still seems to be issues including a config file
+         // in a path containing spaces
+
+         MakePathRelative(temp, topNode.path, cfDir);
+      }
+
+      if(cfDir && cfDir[0] && cfDir[strlen(cfDir)-1] != '/')
+         strcat(cfDir, "/");
+      if(replaceSpaces)
+      {
+         strcpy(temp, cfDir);
+         ReplaceSpaces(cfDir, temp);
+      }
+      return result;
+   }
+
+   bool GetIDECompilerConfigsDir(char * cfDir, bool replaceSpaces, bool makeRelative)
+   {
+      char temp[MAX_LOCATION];
+      bool result = false;
+      strcpy(cfDir, topNode.path);
+      if(ideSettings.compilerConfigsDir && ideSettings.compilerConfigsDir[0])
+      {
+         PathCatSlash(cfDir, ideSettings.compilerConfigsDir);
+         result = true;
+      }
+      else
+      {
+         // Default to <ProjectDir>/.configs if unset
+         PathCatSlash(cfDir, ".configs");
+         result = true;
+      }
+      if(makeRelative)
+      {
+         strcpy(temp, cfDir);
+         // Using a relative path makes it less likely to run into spaces issues
+         // Even with escaped spaces, there still seems to be issues including a config file
+         // in a path containing spaces
+         if(IsPathInsideOf(cfDir, topNode.path))
+            MakePathRelative(temp, topNode.path, cfDir);
+      }
+      if(cfDir && cfDir[0] && cfDir[strlen(cfDir)-1] != '/')
+         strcat(cfDir, "/");
+      if(replaceSpaces)
+      {
+         strcpy(temp, cfDir);
+         ReplaceSpaces(cfDir, temp);
+      }
+      return result;
+   }
+
+   void CatMakeFileName(char * string, ProjectConfig config)
    {
       char projectName[MAX_LOCATION];
-      CompilerConfig compiler = GetCompilerConfig();
       strcpy(projectName, name);
-      if(strcmpi(compiler.name, defaultCompilerName))
-         sprintf(string, "%s-%s-%s.Makefile", projectName, compiler.name, config.name);
-      else
-         sprintf(string, "%s%s%s.Makefile", projectName, config ? "-" : "", config ? config.name : "");
-      delete compiler;
+      sprintf(string, "%s%s%s.Makefile", projectName, config ? "-" : "", config ? config.name : "");
    }
 
 #ifndef MAKEFILE_GENERATOR
@@ -988,7 +1261,8 @@ private:
          ProjectConfig c = null;
          for(i : node.configurations; !strcmpi(i.name, cfg.name)) { c = i; break; }
 
-         if(c && cfg.options.console != c.options.console)
+         if(c && ((c.options && cfg.options && cfg.options.console != c.options.console) ||
+               (!c.options || !cfg.options)))
             cfg.symbolGenModified = true;
 
          cfg.makingModified = true;
@@ -1070,40 +1344,54 @@ private:
       }
       if(ide.ShouldStopBuild())
       {
-         ide.outputView.buildBox.Logf("\nBuild cancelled by user.\n", line);
+         ide.outputView.buildBox.Logf($"\nBuild cancelled by user.\n", line);
          f.Terminate();
       }
    }
 
-   bool ProcessBuildPipeOutput(DualPipe f, DirExpression objDirExp, bool isARun, ProjectNode onlyNode)
+   bool ProcessBuildPipeOutput(DualPipe f, DirExpression objDirExp, bool isARun, ProjectNode onlyNode,
+      CompilerConfig compiler, ProjectConfig config)
    {
       char line[65536];
       bool compiling = false, linking = false, precompiling = false;
       int compilingEC = 0;
       int numErrors = 0, numWarnings = 0;
       bool loggedALine = false;
-      CompilerConfig compiler = GetCompilerConfig();
+      char * configName = config ? config.name : "Common";
       int lenMakeCommand = strlen(compiler.makeCommand);
-      
+
       char cppCommand[MAX_LOCATION];
       char ccCommand[MAX_LOCATION];
+      char cxxCommand[MAX_LOCATION];
+      char stripCommand[MAX_LOCATION];
       char ecpCommand[MAX_LOCATION];
       char eccCommand[MAX_LOCATION];
       char ecsCommand[MAX_LOCATION];
       char earCommand[MAX_LOCATION];
 
       char * cc = compiler.ccCommand;
+      char * cxx = compiler.cxxCommand;
       char * cpp = compiler.cppCommand;
+      char * strip = compiler.cppCommand;
       sprintf(cppCommand, "%s%s%s%s ",
             compiler.ccacheEnabled ? "ccache " : "",
-            compiler.ccacheEnabled && !compiler.distccEnabled ? " " : "",
             compiler.distccEnabled ? "distcc " : "",
+            compiler.gccPrefix ? compiler.gccPrefix : "",
             compiler.cppCommand);
       sprintf(ccCommand, "%s%s%s%s ",
             compiler.ccacheEnabled ? "ccache " : "",
-            compiler.ccacheEnabled && !compiler.distccEnabled ? " " : "",
             compiler.distccEnabled ? "distcc " : "",
+            compiler.gccPrefix ? compiler.gccPrefix : "",
             compiler.ccCommand);
+      sprintf(cxxCommand, "%s%s%s%s ",
+            compiler.ccacheEnabled ? "ccache " : "",
+            compiler.distccEnabled ? "distcc " : "",
+            compiler.gccPrefix ? compiler.gccPrefix : "",
+            compiler.cxxCommand);
+
+      sprintf(stripCommand, "%sstrip ",
+            compiler.gccPrefix ? compiler.gccPrefix : "");
+
       sprintf(ecpCommand, "%s ", compiler.ecpCommand);
       sprintf(eccCommand, "%s ", compiler.eccCommand);
       sprintf(ecsCommand, "%s ", compiler.ecsCommand);
@@ -1119,6 +1407,7 @@ private:
             //printf("Peeking and GetLine...\n");
             if((result = f.Peek()) && (result = f.GetLine(line, sizeof(line)-1)))
             {
+               char * inFileIncludedFrom = strstr(line, stringInFileIncludedFrom);
                if(strstr(line, compiler.makeCommand) == line && line[lenMakeCommand] == ':')
                {
                   char * module = strstr(line, "No rule to make target `");
@@ -1130,27 +1419,27 @@ private:
                      if(end)
                      {
                         *end = '\0';
-                        ide.outputView.buildBox.Logf("   %s: No such file or directory\n", module);
+                        ide.outputView.buildBox.Logf($"   %s: No such file or directory\n", module);
                         // ide.outputView.buildBox.Logf("error: %s\n   No such file or directory\n", module);
-                        numErrors ++;
+                        numErrors++;
                      }
                   }
-                  else
-                  {
+                  //else
+                  //{
                      //ide.outputView.buildBox.Logf("error: %s\n", line);
-                     //numErrors ++;
-                  }
+                     //numErrors++;
+                  //}
                }
                else if(strstr(line, "ear ") == line);
-               else if(strstr(line, "strip ") == line);
-               else if(strstr(line, ccCommand) == line || strstr(line, ecpCommand) == line || strstr(line, eccCommand) == line)
+               else if(strstr(line, stripCommand) == line);
+               else if(strstr(line, ccCommand) == line || strstr(line, cxxCommand) == line || strstr(line, ecpCommand) == line || strstr(line, eccCommand) == line)
                {
                   char moduleName[MAX_FILENAME];
                   byte * tokens[1];
                   char * module;
                   bool isPrecomp = false;
-               
-                  if(strstr(line, ccCommand) == line)
+
+                  if(strstr(line, ccCommand) == line || strstr(line, cxxCommand) == line)
                   {
                      module = strstr(line, " -c ");
                      if(module) module += 4;
@@ -1178,15 +1467,16 @@ private:
                   {
                      if(!compiling && !isPrecomp)
                      {
-                        ide.outputView.buildBox.Logf("Compiling...\n");
+                        ide.outputView.buildBox.Logf($"Compiling...\n");
                         compiling = true;
                      }
                      else if(!precompiling && isPrecomp)
                      {
-                        ide.outputView.buildBox.Logf("Generating symbols...\n");
+                        ide.outputView.buildBox.Logf($"Generating symbols...\n");
                         precompiling = true;
                      }
-                     Tokenize(module, 1, tokens, false);
+                     // Changed escapeBackSlashes here to handle paths with spaces
+                     Tokenize(module, 1, tokens, true); // false);
                      GetLastDirectory(module, moduleName);
                      ide.outputView.buildBox.Logf("%s\n", moduleName);
                   }
@@ -1195,20 +1485,20 @@ private:
                      compiling = false;
                      precompiling = false;
                      linking = true;
-                     ide.outputView.buildBox.Logf("Linking...\n");
+                     ide.outputView.buildBox.Logf($"Linking...\n");
                   }
                   else
                   {
                      ide.outputView.buildBox.Logf("%s\n", line);
-                     numErrors ++;
+                     numErrors++;
                   }
 
                   if(compilingEC) compilingEC--;
                }
                else if(strstr(line, "ar rcs") == line)
-                  ide.outputView.buildBox.Logf("Building library...\n");
+                  ide.outputView.buildBox.Logf($"Building library...\n");
                else if(strstr(line, ecsCommand) == line)
-                  ide.outputView.buildBox.Logf("Writing symbol loader...\n");
+                  ide.outputView.buildBox.Logf($"Writing symbol loader...\n");
                else
                {
                   if(linking || compiling || precompiling)
@@ -1220,27 +1510,29 @@ private:
                      {
                         char moduleName[MAX_LOCATION], temp[MAX_LOCATION];
                         char * pointer;
-                        int len = (int)(colon - line);
+                        char * error;
+                        char * start = inFileIncludedFrom ? line + strlen(stringInFileIncludedFrom) : line;
+                        int len = (int)(colon - start);
                         len = Min(len, MAX_LOCATION-1);
                         // Don't be mistaken by the drive letter colon
                         // Cut module name
-                        // TODO: need to fix colon - line gives char * 
+                        // TODO: need to fix colon - line gives char *
                         // warning: incompatible expression colon - line (char *); expected int
                         /*
                         strncpy(moduleName, line, (int)(colon - line));
                         moduleName[colon - line] = '\0';
                         */
-                        strncpy(moduleName, line, len);
+                        strncpy(moduleName, start, len);
                         moduleName[len] = '\0';
                         // Remove stuff in brackets
                         //bracket = strstr(moduleName, "(");
                         //if(bracket) *bracket = '\0';
-                     
+
                         GetLastDirectory(moduleName, temp);
                         if(linking && (!strcmp(temp, "ld") || !strcmp(temp, "ld.exe")))
                         {
                            numErrors++;
-                           strcpy(moduleName, "Linker Error");
+                           strcpy(moduleName, $"Linker Error");
                         }
                         else
                         {
@@ -1290,7 +1582,7 @@ private:
                            PathCat(fullModuleName, moduleName);
                            MakePathRelative(fullModuleName, ide.workspace.projects.firstIterator.data.topNode.path, fullModuleName);
                            MakeSystemPath(fullModuleName);
-                           ide.outputView.buildBox.Logf("   %s%s\n", fullModuleName, colon);                              
+                           ide.outputView.buildBox.Logf("   %s%s%s\n", inFileIncludedFrom ? stringInFileIncludedFrom : "", fullModuleName, colon);
                         }
                      }
                      else
@@ -1320,7 +1612,7 @@ private:
       }
       if(ide.ShouldStopBuild())
       {
-         ide.outputView.buildBox.Logf("\nBuild cancelled by user.\n", line);
+         ide.outputView.buildBox.Logf($"\nBuild cancelled by user.\n", line);
          f.Terminate();
       }
       else if(loggedALine || !isARun)
@@ -1328,32 +1620,29 @@ private:
          if(f.GetExitCode() && !numErrors)
          {
             bool result = f.GetLine(line, sizeof(line)-1);
-            ide.outputView.buildBox.Logf("Fatal Error: child process terminated unexpectedly\n");
+            ide.outputView.buildBox.Logf($"Fatal Error: child process terminated unexpectedly\n");
          }
          else
          {
             if(!onlyNode)
-               ide.outputView.buildBox.Logf("\n%s (%s) - ", targetFileName, configName);
+               ide.outputView.buildBox.Logf("\n%s (%s) - ", GetTargetFileName(config), configName);
             if(numErrors)
-               ide.outputView.buildBox.Logf("%d %s, ", numErrors, (numErrors > 1) ? "errors" : "error");
+               ide.outputView.buildBox.Logf("%d %s, ", numErrors, (numErrors > 1) ? $"errors" : $"error");
             else
-               ide.outputView.buildBox.Logf("no error, ");
+               ide.outputView.buildBox.Logf($"no error, ");
    
             if(numWarnings)
-               ide.outputView.buildBox.Logf("%d %s\n", numWarnings, (numWarnings > 1) ? "warnings" : "warning");
+               ide.outputView.buildBox.Logf("%d %s\n", numWarnings, (numWarnings > 1) ? $"warnings" : $"warning");
             else
-               ide.outputView.buildBox.Logf("no warning\n");
+               ide.outputView.buildBox.Logf($"no warning\n");
          }
       }
-      
-      delete compiler;
       return numErrors == 0;
    }
 
-   void ProcessCleanPipeOutput(DualPipe f)
+   void ProcessCleanPipeOutput(DualPipe f, CompilerConfig compiler, ProjectConfig config)
    {
       char line[65536];
-      CompilerConfig compiler = GetCompilerConfig();
       int lenMakeCommand = strlen(compiler.makeCommand);
       while(!f.Eof())
       {
@@ -1384,10 +1673,9 @@ private:
             app.Wait();
          //Sleep(1.0 / PEEK_RESOLUTION);
       }
-      delete compiler;
    }
 
-   bool Build(bool isARun, ProjectNode onlyNode)
+   bool Build(bool isARun, ProjectNode onlyNode, CompilerConfig compiler, ProjectConfig config)
    {
       bool result = false;
       DualPipe f;
@@ -1395,25 +1683,28 @@ private:
       char makeTarget[MAX_LOCATION] = "";
       char makeFile[MAX_LOCATION];
       char makeFilePath[MAX_LOCATION];
-      char oldPath[MAX_LOCATION * 65];
       char configName[MAX_LOCATION];
-      CompilerConfig compiler = GetCompilerConfig();
-      DirExpression objDirExp = objDir;
+      DirExpression objDirExp = GetObjDir(compiler, config);
+      PathBackup pathBackup { };
+      bool crossCompiling = (compiler.targetPlatform != GetRuntimePlatform());
+      char * targetPlatform = crossCompiling ? (char *)compiler.targetPlatform : "";
 
       int numJobs = compiler.numJobs;
       char command[MAX_LOCATION];
+      char * compilerName;
 
-      GetEnvironment("PATH", oldPath, sizeof(oldPath));
+      compilerName = CopyString(compiler.name);
+      CamelCase(compilerName);
 
-      strcpy(configName, this.configName);
-      
-      SetPath(false); //true
-      CatTargetFileName(targetFileName);
+      strcpy(configName, config ? config.name : "Common");
+
+      SetPath(false, compiler, config); //true
+      CatTargetFileName(targetFileName, compiler, config);
 
       strcpy(makeFilePath, topNode.path);
-      CatMakeFileName(makeFile);
+      CatMakeFileName(makeFile, config);
       PathCatSlash(makeFilePath, makeFile);
-      
+
       // TODO: TEST ON UNIX IF \" around makeTarget is ok
       if(onlyNode)
       {
@@ -1425,11 +1716,24 @@ private:
          {
             int len;
             char pushD[MAX_LOCATION];
+            char cfDir[MAX_LOCATION];
+            GetIDECompilerConfigsDir(cfDir, true, true);
             GetWorkingDir(pushD, sizeof(pushD));
             ChangeWorkingDir(topNode.path);
             // Create object dir if it does not exist already
             if(!FileExists(objDirExp.dir).isDirectory)
-               Execute("%s objdir -C \"%s\" -f \"%s\"", compiler.makeCommand, topNode.path, makeFilePath);
+            {
+               sprintf(command, "%s CF_DIR=\"%s\"%s%s COMPILER=%s objdir -C \"%s\" -f \"%s\"",
+                     compiler.makeCommand, cfDir,
+                     crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform,
+                     compilerName, topNode.path, makeFilePath);
+#ifdef _DEBUG
+               PrintLn(command);
+               ide.outputView.buildBox.Logf("command: %s\n", command);
+#endif
+               Execute(command);
+            }
+
             ChangeWorkingDir(pushD);
 
             PathCatSlash(makeTarget+1, objDirExp.dir);
@@ -1452,6 +1756,10 @@ private:
 
          sprintf(command, "%s /useenv /nologo /logcommands %s.sln %s|Win32", compiler.makeCommand, name, config.name);
          ide.outputView.buildBox.Logf("command: %s\n", command);
+#ifdef _DEBUG
+         PrintLn(command);
+         ide.outputView.buildBox.Logf("command: %s\n", command);
+#endif
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
             ProcessPipeOutputRaw(f);
@@ -1462,41 +1770,57 @@ private:
       }
       else
       {
-         sprintf(command, "%s -j%d %s%s%s -C \"%s\" -f \"%s\"", compiler.makeCommand, numJobs,
+         char cfDir[MAX_LOCATION];
+         GetIDECompilerConfigsDir(cfDir, true, true);
+         sprintf(command, "%s CF_DIR=\"%s\"%s%s COMPILER=%s -j%d %s%s%s -C \"%s\" -f \"%s\"",
+               compiler.makeCommand, cfDir,
+               crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform,
+               compilerName, numJobs,
                compiler.ccacheEnabled ? "CCACHE=y " : "",
                compiler.distccEnabled ? "DISTCC=y " : "",
                makeTarget, topNode.path, makeFilePath);
+#ifdef _DEBUG
+         PrintLn(command);
+         ide.outputView.buildBox.Logf("command: %s\n", command);
+#endif
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
-            result = ProcessBuildPipeOutput(f, objDirExp, isARun, onlyNode);
+            result = ProcessBuildPipeOutput(f, objDirExp, isARun, onlyNode, compiler, config);
             delete f;
          }
          else
-            ide.outputView.buildBox.Logf("Error executing make (%s) command\n", compiler.makeCommand);
+         {
+            ide.outputView.buildBox.Logf($"Error executing make (%s) command\n", compiler.makeCommand);
+#ifndef _DEBUG
+            ide.outputView.buildBox.Logf("command: %s\n", command);
+#endif
+         }
       }
 
-      SetEnvironment("PATH", oldPath);
-
+      delete pathBackup;
       delete objDirExp;
-      delete compiler;
+      delete compilerName;
       return result;
    }
 
-   void Clean()
+   void Clean(CompilerConfig compiler, ProjectConfig config, bool realclean)
    {
-      char oldPath[MAX_LOCATION * 65];
       char makeFile[MAX_LOCATION];
       char makeFilePath[MAX_LOCATION];
       char command[MAX_LOCATION];
+      char * compilerName;
       DualPipe f;
-      CompilerConfig compiler = GetCompilerConfig();
+      PathBackup pathBackup { };
+      bool crossCompiling = (compiler.targetPlatform != GetRuntimePlatform());
+      char * targetPlatform = crossCompiling ? (char *)compiler.targetPlatform : "";
 
-      GetEnvironment("PATH", oldPath, sizeof(oldPath));
+      compilerName = CopyString(compiler.name);
+      CamelCase(compilerName);
 
-      SetPath(false);
+      SetPath(false, compiler, config);
 
       strcpy(makeFilePath, topNode.path);
-      CatMakeFileName(makeFile);
+      CatMakeFileName(makeFile, config);
       PathCatSlash(makeFilePath, makeFile);
       
       if(compiler.type.isVC)
@@ -1508,6 +1832,10 @@ private:
          
          sprintf(command, "%s /useenv /clean /nologo /logcommands %s.sln %s|Win32", compiler.makeCommand, name, config.name);
          ide.outputView.buildBox.Logf("command: %s\n", command);
+#ifdef _DEBUG
+         PrintLn(command);
+         ide.outputView.buildBox.Logf("command: %s\n", command);
+#endif
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
             ProcessPipeOutputRaw(f);
@@ -1519,29 +1847,36 @@ private:
       }
       else
       {
-         sprintf(command, "%s clean -C \"%s\" -f \"%s\"", compiler.makeCommand, topNode.path, makeFilePath);
+         char cfDir[MAX_LOCATION];
+         GetIDECompilerConfigsDir(cfDir, true, true);
+         sprintf(command, "%s CF_DIR=\"%s\"%s%s COMPILER=%s %sclean -C \"%s\" -f \"%s\"",
+               compiler.makeCommand, cfDir,
+               crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform,
+               compilerName, realclean ? "real" : "", topNode.path, makeFilePath);
+#ifdef _DEBUG
+         PrintLn(command);
+         ide.outputView.buildBox.Logf("command: %s\n", command);
+#endif
          if((f = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
          {
-            ide.outputView.buildBox.Tell("Deleting target and object files...");
-            ProcessCleanPipeOutput(f);
+            ide.outputView.buildBox.Tell($"Deleting target and object files...");
+            ProcessCleanPipeOutput(f, compiler, config);
             delete f;
 
-            ide.outputView.buildBox.Logf("Target and object files deleted\n");
+            ide.outputView.buildBox.Logf($"Target and object files deleted\n");
          }
       }
 
-      SetEnvironment("PATH", oldPath);
-      delete compiler;
+      delete pathBackup;
+      delete compilerName;
    }
 
-   void Run(char * args)
+   void Run(char * args, CompilerConfig compiler, ProjectConfig config)
    {   
-      char target[MAX_LOCATION * 65], oldDirectory[MAX_LOCATION];
-      char oldPath[MAX_LOCATION * 65];
-      DirExpression targetDirExp = targetDir;
-      CompilerConfig compiler = GetCompilerConfig();
-
-      GetEnvironment("PATH", oldPath, sizeof(oldPath));
+      String target = new char[maxPathLen];
+      char oldDirectory[MAX_LOCATION];
+      DirExpression targetDirExp = GetTargetDir(compiler, config);
+      PathBackup pathBackup { };
 
       // Build(project, ideMain, true, null);
 
@@ -1551,7 +1886,7 @@ private:
       strcpy(target, "");
    #endif
       PathCatSlash(target, targetDirExp.dir);
-      CatTargetFileName(target);
+      CatTargetFileName(target, compiler, config);
       sprintf(target, "%s %s", target, args);
       GetWorkingDir(oldDirectory, MAX_LOCATION);
 
@@ -1565,7 +1900,7 @@ private:
       else
          ChangeWorkingDir(topNode.path);
       // ChangeWorkingDir(topNode.path);
-      SetPath(true);
+      SetPath(true, compiler, config);
       if(compiler.execPrefixCommand)
       {
          char * prefixedTarget = new char[strlen(compiler.execPrefixCommand) + strlen(target) + 2];
@@ -1580,26 +1915,26 @@ private:
          Execute(target);
 
       ChangeWorkingDir(oldDirectory);
-      SetEnvironment("PATH", oldPath);
+      delete pathBackup;
 
       delete targetDirExp;
-      delete compiler;
+      delete target;
    }
 
-   void Compile(ProjectNode node)
+   void Compile(ProjectNode node, CompilerConfig compiler, ProjectConfig config)
    {
-      Build(false, node);
+      Build(false, node, compiler, config);
    }
 #endif
 
-   void GetMakefileTargetFileName(TargetTypes targetType, char * fileName)
+   void GetMakefileTargetFileName(TargetTypes targetType, char * fileName, ProjectConfig config)
    {
-      char s[MAX_LOCATION];
       fileName[0] = '\0';
       if(targetType == staticLibrary || targetType == sharedLibrary)
          strcat(fileName, "$(LP)");
-      ReplaceSpaces(s, targetFileName);
-      strcat(fileName, s);
+      // !!! ReplaceSpaces must be done after all PathCat calls !!!
+      // ReplaceSpaces(s, GetTargetFileName(config));
+      strcat(fileName, GetTargetFileName(config));
       switch(targetType)
       {
          case executable:
@@ -1614,412 +1949,605 @@ private:
       }
    }
 
-   bool GenerateMakefile(char * altMakefilePath, bool noResources, char * includemkPath)
+   bool GenerateCrossPlatformMk()
    {
       bool result = false;
-      char filePath[MAX_LOCATION];
-      char makeFile[MAX_LOCATION];
-      CompilerConfig compiler = GetCompilerConfig();
-      /*char oldPath[MAX_LOCATION * 65];
-      char oldDirectory[MAX_LOCATION];*/
-      File f = null;
+      char path[MAX_LOCATION];
 
-      if(!altMakefilePath)
+      if(!GetProjectCompilerConfigsDir(path, false, false))
+         GetIDECompilerConfigsDir(path, false, false);
+
+      if(!FileExists(path).isDirectory)
       {
-         strcpy(filePath, topNode.path);
-         CatMakeFileName(makeFile);
-         PathCatSlash(filePath, makeFile);
+         MakeDir(path);
+         {
+            char dirName[MAX_FILENAME];
+            GetLastDirectory(path, dirName);
+            if(!strcmp(dirName, ".configs"))
+               FileSetAttribs(path, FileAttribs { isHidden = true });
+         }
       }
+      PathCatSlash(path, "crossplatform.mk");
 
-#if defined(__WIN32__) && !defined(MAKEFILE_GENERATOR)
-      if(compiler.type.isVC)
+      if(FileExists(path))
+         DeleteFile(path);
       {
-         GenerateVSSolutionFile(this);
-         GenerateVCProjectFile(this);
+         File include = FileOpen(":crossplatform.mk", read);
+         if(include)
+         {
+            File f = FileOpen(path, write);
+            if(f)
+            {
+               for(; !include.Eof(); )
+               {
+                  char buffer[4096];
+                  int count = include.Read(buffer, 1, 4096);
+                  f.Write(buffer, 1, count);
+               }
+               delete f;
+
+               result = true;
+            }
+            delete include;
+         }
       }
-      else
-#endif
+      return result;
+   }
+
+   bool GenerateCompilerCf(CompilerConfig compiler)
+   {
+      bool result = false;
+      char path[MAX_LOCATION];
+      char * name;
+      char * compilerName;
+      bool gccCompiler = compiler.ccCommand && (strstr(compiler.ccCommand, "gcc") != null || strstr(compiler.ccCommand, "g++") != null);
+      Platform platform = compiler.targetPlatform;
+
+      compilerName = CopyString(compiler.name);
+      CamelCase(compilerName);
+      name = PrintString(platform, "-", compilerName, ".cf");
+
+      if(!GetProjectCompilerConfigsDir(path, false, false))
+         GetIDECompilerConfigsDir(path, false, false);
+
+      if(!FileExists(path).isDirectory)
+      {
+         MakeDir(path);
+         {
+            char dirName[MAX_FILENAME];
+            GetLastDirectory(path, dirName);
+            if(!strcmp(dirName, ".configs"))
+               FileSetAttribs(path, FileAttribs { isHidden = true });
+         }
+      }
+      PathCatSlash(path, name);
+
+      if(FileExists(path))
+         DeleteFile(path);
+      {
+         File f = FileOpen(path, write);
+         if(f)
+         {
+            f.Puts("# TOOLCHAIN\n");
+            f.Puts("\n");
+
+            if(compiler.gccPrefix && compiler.gccPrefix[0])
+            {
+               f.Printf("GCC_PREFIX := %s\n", compiler.gccPrefix);
+               f.Puts("\n");
+            }
+            if(compiler.sysroot && compiler.sysroot[0])
+            {
+               f.Printf("SYSROOT := %s\n", compiler.sysroot);
+               f.Puts("_SYSROOT := $(space)--sysroot=$(SYSROOT)\n");
+               f.Puts("\n");
+            }
+
+            //f.Printf("SHELL := %s\n", "sh"/*compiler.shellCommand*/); // is this really needed?
+            f.Printf("CPP := $(CCACHE_COMPILE)$(DISTCC_COMPILE)$(GCC_PREFIX)%s$(_SYSROOT)\n", compiler.cppCommand);
+            f.Printf("CC := $(CCACHE_COMPILE)$(DISTCC_COMPILE)$(GCC_PREFIX)%s$(_SYSROOT)\n", compiler.ccCommand);
+            f.Printf("CXX := $(CCACHE_COMPILE)$(DISTCC_COMPILE)$(GCC_PREFIX)%s$(_SYSROOT)\n", compiler.cxxCommand);
+            f.Printf("ECP := %s\n", compiler.ecpCommand);
+            f.Printf("ECC := %s$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)\n", compiler.eccCommand);
+            f.Printf("ECS := %s$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)\n", compiler.ecsCommand);
+            f.Printf("EAR := %s\n", compiler.earCommand);
+
+            f.Puts("AS := $(GCC_PREFIX)as\n");
+            f.Puts("LD := $(GCC_PREFIX)ld\n");
+            f.Puts("AR := $(GCC_PREFIX)ar\n");
+            f.Puts("STRIP := $(GCC_PREFIX)strip\n");
+            f.Puts("UPX := upx\n");
+            f.Puts("\n");
+
+            if(compiler.environmentVars && compiler.environmentVars.count)
+            {
+               f.Puts("# ENVIRONMENT VARIABLES\n");
+               f.Puts("\n");
+               for(e : compiler.environmentVars)
+               {
+                  f.Printf("export %s := %s\n", e.name, e.string);
+               }
+            }
+
+            f.Puts("UPXFLAGS = -9\n"); // TOFEAT: Compression Level Option? Other UPX Options?
+            f.Puts("\n");
+
+            f.Puts("EARFLAGS = aw\n");
+            f.Puts("\n");
+
+            f.Puts("# HARD CODED TARGET_PLATFORM-SPECIFIC OPTIONS\n");
+            f.Printf("LDFLAGS +=$(if $(%s), -Wl$(comma)--no-undefined,)\n", PlatformToMakefileTargetVariable(tux));
+            f.Puts("\n");
+
+            // JF's
+            f.Printf("LDFLAGS +=$(if $(%s), -framework cocoa -framework OpenGL,)\n", PlatformToMakefileTargetVariable(apple));
+
+            if(gccCompiler)
+            {
+               f.Puts("\nCFLAGS += -fmessage-length=0\n");
+            }
+
+            if(compiler.includeDirs && compiler.includeDirs.count)
+            {
+               f.Puts("\nCFLAGS +=");
+               OutputListOption(f, gccCompiler ? "isystem " : "I", compiler.includeDirs, lineEach, true);
+               f.Puts("\n");
+            }
+            if(compiler.prepDirectives && compiler.prepDirectives.count)
+            {
+               f.Puts("\nCFLAGS +=");
+               OutputListOption(f, "D", compiler.prepDirectives, inPlace, true);
+               f.Puts("\n");
+            }
+            if(compiler.libraryDirs && compiler.libraryDirs.count)
+            {
+               f.Puts("\nLDFLAGS +=");
+               OutputListOption(f, "L", compiler.libraryDirs, lineEach, true);
+               // We would need a bool option to know whether we want to add to rpath as well...
+               // OutputListOption(f, "Wl,-rpath ", compiler.libraryDirs, lineEach, true);
+               f.Puts("\n");
+            }
+            if(compiler.excludeLibs && compiler.excludeLibs.count)
+            {
+               f.Puts("\nEXCLUDED_LIBS =");
+               for(l : compiler.excludeLibs)
+               {
+                  f.Puts(" ");
+                  f.Puts(l);
+               }
+            }
+            if(compiler.linkerFlags && compiler.linkerFlags.count)
+            {
+               f.Puts("\nLDFLAGS +=");
+               OutputListOption(f, "Wl,", compiler.linkerFlags, inPlace, true);
+               f.Puts("\n");
+            }
+            f.Printf("\nFORCE_64_BIT := %s", compiler.supportsBitDepth ? "-m64" : "");
+            f.Printf("\nFORCE_32_BIT := %s", compiler.supportsBitDepth ? "-m32" : "");
+            f.Puts("\n");
+
+            delete f;
+
+            result = true;
+         }
+      }
+      delete name;
+      delete compilerName;
+      return result;
+   }
+
+   bool GenerateMakefile(char * altMakefilePath, bool noResources, char * includemkPath, ProjectConfig config)
+   {
+      bool result = false;
+      char filePath[MAX_LOCATION];
+      char makeFile[MAX_LOCATION];
+      // PathBackup pathBackup { };
+      // char oldDirectory[MAX_LOCATION];
+      File f = null;
+
+      if(!altMakefilePath)
+      {
+         strcpy(filePath, topNode.path);
+         CatMakeFileName(makeFile, config);
+         PathCatSlash(filePath, makeFile);
+      }
+
       f = FileOpen(altMakefilePath ? altMakefilePath : filePath, write);
 
-      /*GetEnvironment("PATH", oldPath, sizeof(oldPath));
-      SetPath(false);
+      /*SetPath(false, compiler, config);
       GetWorkingDir(oldDirectory, MAX_LOCATION);
       ChangeWorkingDir(topNode.path);*/
 
       if(f)
       {
-         char temp[MAX_LOCATION];
-         char target[MAX_LOCATION];
-         char targetNoSpaces[MAX_LOCATION];
+         bool test;
+         int ifCount;
+         Platform platform;
+         char targetDir[MAX_LOCATION];
          char objDirExpNoSpaces[MAX_LOCATION];
          char objDirNoSpaces[MAX_LOCATION];
          char resDirNoSpaces[MAX_LOCATION];
          char targetDirExpNoSpaces[MAX_LOCATION];
          char fixedModuleName[MAX_FILENAME];
          char fixedConfigName[MAX_FILENAME];
-         char fixedCompilerName[MAX_FILENAME];
          int c, len;
+         // Non-zero if we're building eC code
+         // We'll have to be careful with this when merging configs where eC files can be excluded in some configs and included in others
          int numCObjects = 0;
+         int numObjects = 0;
+         bool containsCXX = false; // True if the project contains a C++ file
          bool sameObjTargetDirs;
-         DirExpression objDirExp = objDir;
-
-         bool crossCompiling = compiler.targetPlatform != GetRuntimePlatform();
-         bool gccCompiler = compiler.ccCommand && strstr(compiler.ccCommand, "gcc") != null;
-         bool tccCompiler = compiler.ccCommand && strstr(compiler.ccCommand, "tcc") != null;
-         bool defaultPreprocessor = compiler.cppCommand && (strstr(compiler.cppCommand, "gcc") != null || compiler.cppCommand && strstr(compiler.cppCommand, "cpp") != null);
+         String objDirExp = GetObjDirExpression(config);
+         TargetTypes targetType = GetTargetType(config);
 
-         int objectsParts, cobjectsParts, symbolsParts, importsParts;
+         char cfDir[MAX_LOCATION];
+         int objectsParts = 0, eCsourcesParts = 0;
          Array<String> listItems { };
          Map<String, int> varStringLenDiffs { };
          Map<String, NameCollisionInfo> namesInfo { };
+         bool forceBitDepth = false;
+
+         Map<String, int> cflagsVariations { };
+         Map<int, int> nodeCFlagsMapping { };
 
-         ReplaceSpaces(objDirNoSpaces, objDirExp.dir);
-         strcpy(temp, targetDirExpression);
-         ReplaceSpaces(targetDirExpNoSpaces, temp);
-         GetMakefileTargetFileName(targetType, target);
-         PathCatSlash(temp, target);
-         ReplaceSpaces(targetNoSpaces, temp);
+         Map<String, int> ecflagsVariations { };
+         Map<int, int> nodeECFlagsMapping { };
 
-         strcpy(objDirExpNoSpaces, objDirExpression);
+         ReplaceSpaces(objDirNoSpaces, objDirExp);
+         strcpy(targetDir, GetTargetDirExpression(config));
+         ReplaceSpaces(targetDirExpNoSpaces, targetDir);
+
+         strcpy(objDirExpNoSpaces, GetObjDirExpression(config));
          ChangeCh(objDirExpNoSpaces, '\\', '/'); // TODO: this is a hack, paths should never include win32 path seperators - fix this in ProjectSettings and ProjectLoad instead
          ReplaceSpaces(objDirExpNoSpaces, objDirExpNoSpaces);
          ReplaceSpaces(resDirNoSpaces, resNode.path ? resNode.path : "");
-         //ReplaceSpaces(fixedPrjName, name);
          ReplaceSpaces(fixedModuleName, moduleName);
-         ReplaceSpaces(fixedConfigName, configName);
-         ReplaceSpaces(fixedCompilerName, compiler.name);
-         //CamelCase(fixedModuleName); // case is important for static linking
+         ReplaceSpaces(fixedConfigName, GetConfigName(config));
          CamelCase(fixedConfigName);
-         CamelCase(fixedCompilerName);
 
          sameObjTargetDirs = !fstrcmp(objDirExpNoSpaces, targetDirExpNoSpaces);
 
-         f.Printf(".PHONY: all objdir%s clean distclean\n\n", sameObjTargetDirs ? "" : " targetdir");
+         f.Printf(".PHONY: all objdir%s clean realclean distclean\n\n", sameObjTargetDirs ? "" : " targetdir");
 
-         f.Printf("# CONTENT\n\n");
+         f.Puts("# CORE VARIABLES\n\n");
 
          f.Printf("MODULE := %s\n", fixedModuleName);
          //f.Printf("VERSION = %s\n", version);
          f.Printf("CONFIG := %s\n", fixedConfigName);
-         f.Printf("COMPILER := %s\n", fixedCompilerName);
-         if(crossCompiling)
-            f.Printf("PLATFORM = %s\n", (char *)compiler.targetPlatform);
-         f.Printf("TARGET_TYPE = %s\n", targetType == executable ? "executable" :
-               (targetType == sharedLibrary ? "sharedlib" : (targetType == staticLibrary ? "staticlib" : "unknown")));
-         f.Printf("\n");
+         f.Puts("ifndef COMPILER\n" "COMPILER := default\n" "endif\n");
+         f.Puts("\n");
 
-         f.Printf("OBJ = %s%s\n\n", objDirExpNoSpaces, objDirExpNoSpaces[0] ? "/" : "");
-
-         f.Printf("RES = %s%s\n\n", resDirNoSpaces, resDirNoSpaces[0] ? "/" : "");
+         test = GetTargetTypeIsSetByPlatform(config);
+         if(test)
+         {
+            ifCount = 0;
+            for(platform = (Platform)1; platform < Platform::enumSize; platform++)
+            {
+               TargetTypes targetType;
+               PlatformOptions projectPOs, configPOs;
+               MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
+               targetType = platformTargetType;
+               if(targetType)
+               {
+                  if(ifCount)
+                     f.Puts("else\n");
+                  ifCount++;
+                  f.Printf("ifdef %s\n", PlatformToMakefileTargetVariable(platform));
+                  f.Printf("TARGET_TYPE = %s\n", TargetTypeToMakefileVariable(targetType));
+               }
+            }
+            f.Puts("else\n");
+         }
+         f.Printf("TARGET_TYPE = %s\n", TargetTypeToMakefileVariable(targetType));
+         if(test)
+         {
+            if(ifCount)
+            {
+               for(c = 0; c < ifCount; c++)
+                  f.Puts("endif\n");
+            }
+         }
+         f.Puts("\n");
 
-         if(targetType == executable)
-            f.Printf("CONSOLE = %s\n\n", console ? "-mconsole" : "-mwindows");
+         f.Puts("# FLAGS\n\n");
 
-         f.Printf("TARGET = %s\n\n", targetNoSpaces);
+         f.Puts("ECFLAGS =\n");
+         f.Puts("ifndef DEBIAN_PACKAGE\n" "CFLAGS =\n" "LDFLAGS =\n" "endif\n");
+         f.Puts("CECFLAGS =\n");
+         f.Puts("OFLAGS =\n");
+         f.Puts("LIBS =\n");
+         f.Puts("\n");
 
-         varStringLenDiffs["$(OBJ)"] = strlen(objDirNoSpaces) - 6;
+         f.Puts("ifdef DEBUG\n" "NOSTRIP := y\n" "endif\n");
+         f.Puts("\n");
 
-         topNode.GenMakefileGetNameCollisionInfo(namesInfo);
+         // Important: We cannot use this ifdef anymore, EXECUTABLE_TARGET is not yet defined. It's embedded in the crossplatform.mk EXECUTABLE
+         //f.Puts("ifdef EXECUTABLE_TARGET\n");
+         f.Printf("CONSOLE = %s\n", GetConsole(config) ? "-mconsole" : "-mwindows");
+         //f.Puts("endif\n");
+         f.Puts("\n");
 
-         numCObjects = topNode.GenMakefilePrintNode(f, this, objects, namesInfo, listItems);
-         if(numCObjects)
-            listItems.Add(CopyString("$(OBJ)$(MODULE).main$(O)"));
-         objectsParts = OutputFileList(f, "OBJECTS", listItems, varStringLenDiffs);
+         f.Puts("# INCLUDES\n\n");
 
-         topNode.GenMakefilePrintNode(f, this, cObjects, namesInfo, listItems);
-         cobjectsParts = OutputFileList(f, "COBJECTS", listItems, varStringLenDiffs);
+         if(compilerConfigsDir && compilerConfigsDir[0])
+         {
+            strcpy(cfDir, compilerConfigsDir);
+            if(cfDir && cfDir[0] && cfDir[strlen(cfDir)-1] != '/')
+               strcat(cfDir, "/");
+         }
+         else
+         {
+            GetIDECompilerConfigsDir(cfDir, true, true);
+            // Use CF_DIR environment variable for absolute paths only
+            if(cfDir[0] == '/' || (cfDir[0] && cfDir[1] == ':'))
+               strcpy(cfDir, "$(CF_DIR)");
+         }
 
-         topNode.GenMakefilePrintNode(f, this, symbols, null, listItems);
-         symbolsParts = OutputFileList(f, "SYMBOLS", listItems, varStringLenDiffs);
+         f.Printf("_CF_DIR = %s\n", cfDir);
+         f.Puts("\n");
 
-         topNode.GenMakefilePrintNode(f, this, imports, null, listItems);
-         importsParts = OutputFileList(f, "IMPORTS", listItems, varStringLenDiffs);
+         f.Printf("include %s\n", includemkPath ? includemkPath : "$(_CF_DIR)crossplatform.mk");
+         f.Puts("include $(_CF_DIR)$(TARGET_PLATFORM)-$(COMPILER).cf\n");
+         f.Puts("\n");
 
-         topNode.GenMakefilePrintNode(f, this, sources, null, listItems);
-         OutputFileList(f, "SOURCES", listItems, varStringLenDiffs);
+         f.Puts("# POST-INCLUDES VARIABLES\n\n");
 
-         if(!noResources)
-            resNode.GenMakefilePrintNode(f, this, resources, null, listItems);
-         OutputFileList(f, "RESOURCES", listItems, varStringLenDiffs);
+         f.Printf("OBJ = %s%s\n", objDirExpNoSpaces, objDirExpNoSpaces[0] ? "/" : "");
+         f.Puts("\n");
 
-         if(includemkPath)
-         {
-            f.Printf("# CROSS-PLATFORM MAGIC\n\n");
+         f.Printf("RES = %s%s\n", resDirNoSpaces, resDirNoSpaces[0] ? "/" : "");
+         f.Puts("\n");
 
-            f.Printf("include %s\n\n", includemkPath);
-         }
-         else
+         // test = GetTargetTypeIsSetByPlatform(config);
          {
-            File include = FileOpen(":include.mk", read);
-            if(include)
+            char target[MAX_LOCATION];
+            char targetNoSpaces[MAX_LOCATION];
+            if(test)
             {
-               for(; !include.Eof(); )
+               TargetTypes type;
+               ifCount = 0;
+               for(type = (TargetTypes)1; type < TargetTypes::enumSize; type++)
                {
-                  char buffer[4096];
-                  int count = include.Read(buffer, 1, 4096);
-                  f.Write(buffer, 1, count);
+                  if(type != targetType)
+                  {
+                     if(ifCount)
+                        f.Puts("else\n");
+                     ifCount++;
+                     f.Printf("ifeq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(type));
+
+                     GetMakefileTargetFileName(type, target, config);
+                     strcpy(targetNoSpaces, targetDir);
+                     PathCatSlash(targetNoSpaces, target);
+                     ReplaceSpaces(targetNoSpaces, targetNoSpaces);
+                     f.Printf("TARGET = %s\n", targetNoSpaces);
+                  }
                }
-               delete include;
+               f.Puts("else\n");
             }
-         }
-
-         f.Printf("\n");
+            GetMakefileTargetFileName(targetType, target, config);
+            strcpy(targetNoSpaces, targetDir);
+            PathCatSlash(targetNoSpaces, target);
+            ReplaceSpaces(targetNoSpaces, targetNoSpaces);
+            f.Printf("TARGET = %s\n", targetNoSpaces);
 
-         if(strcmpi(compiler.cppCommand, "cpp") ||
-               strcmpi(compiler.ccCommand,  "gcc") ||
-               strcmpi(compiler.ecpCommand, "ecp") ||
-               strcmpi(compiler.eccCommand, "ecc") ||
-               strcmpi(compiler.ecsCommand, "ecs") || crossCompiling ||
-               strcmpi(compiler.earCommand, "ear"))
-         {
-            f.Printf("# TOOLCHAIN\n\n");
-
-            //f.Printf("SHELL := %s\n", "ar"/*compiler.arCommand*/); // is this really needed?
-            if(strcmpi(compiler.cppCommand, "cpp"))
-               f.Printf("CPP := $(CCACHE_COMPILE) $(DISTCC_COMPILE) %s\n", compiler.cppCommand);
-            if(strcmpi(compiler.ccCommand,  "gcc"))
-               f.Printf("CC := $(CCACHE_COMPILE) $(DISTCC_COMPILE) %s\n", compiler.ccCommand);
-            if(strcmpi(compiler.ecpCommand, "ecp"))
-               f.Printf("ECP := %s\n", compiler.ecpCommand);
-            if(strcmpi(compiler.eccCommand, "ecc"))
-               f.Printf("ECC := %s\n", compiler.eccCommand);
-            if(strcmpi(compiler.ecsCommand, "ecs") || crossCompiling)
+            if(test)
             {
-               f.Printf("ECS := %s%s%s\n", compiler.ecsCommand,
-                     crossCompiling ? " -t " : "", crossCompiling ? (char*)compiler.targetPlatform : "");
+               if(ifCount)
+               {
+                  for(c = 0; c < ifCount; c++)
+                     f.Puts("endif\n");
+               }
             }
-            if(strcmpi(compiler.earCommand, "ear"))
-               f.Printf("EAR := %s\n", compiler.earCommand);
-            f.Printf("\n");
          }
+         f.Puts("\n");
 
-         f.Printf("# FLAGS\n\n");
+         // Use something fixed here, to not cause Makefile differences across compilers...
+         varStringLenDiffs["$(OBJ)"] = 30; // strlen("obj/memoryGuard.android.gcc-4.6.2") - 6;
+         // varStringLenDiffs["$(OBJ)"] = strlen(objDirNoSpaces) - 6;
+
+         topNode.GenMakefileGetNameCollisionInfo(namesInfo, config);
 
-         f.Printf("CFLAGS =");
-         if(gccCompiler)
          {
-            f.Printf(" -fmessage-length=0");
-            switch(optimization)
+            int c;
+            char * map[5][2] = { { "COBJECTS", "C" }, { "SYMBOLS", "S" }, { "IMPORTS", "I" }, { "ECOBJECTS", "O" }, { "BOWLS", "B" } };
+
+            numCObjects = topNode.GenMakefilePrintNode(f, this, eCsources, namesInfo, listItems, config, null);
+            if(numCObjects)
             {
-               case speed:
-                  f.Printf(" -O2");
-                  f.Printf(" -ffast-math");
-                  break;
-               case size:
-                  f.Printf(" -Os");
-                  break;
+               eCsourcesParts = OutputFileList(f, "_ECSOURCES", listItems, varStringLenDiffs, null);
+
+               f.Puts("ECSOURCES = $(call shwspace,$(_ECSOURCES))\n");
+               if(eCsourcesParts > 1)
+               {
+                  for(c = 1; c <= eCsourcesParts; c++)
+                     f.Printf("ECSOURCES%d = $(call shwspace,$(_ECSOURCES%d))\n", c, c);
+               }
+               f.Puts("\n");
+
+               for(c = 0; c < 5; c++)
+               {
+                  if(eCsourcesParts > 1)
+                  {
+                     int n;
+                     f.Printf("%s =", map[c][0]);
+                     for(n = 1; n <= eCsourcesParts; n++)
+                        f.Printf(" $(%s%d)", map[c][0], n);
+                     f.Puts("\n");
+                     for(n = 1; n <= eCsourcesParts; n++)
+                        f.Printf("%s%d = $(call shwspace,$(addprefix $(OBJ),$(patsubst %%.ec,%%$(%s),$(notdir $(_ECSOURCES%d)))))\n", map[c][0], n, map[c][1], n);
+                  }
+                  else if(eCsourcesParts == 1)
+                     f.Printf("%s = $(call shwspace,$(addprefix $(OBJ),$(patsubst %%.ec,%%$(%s),$(notdir $(_ECSOURCES)))))\n", map[c][0], map[c][1]);
+                  f.Puts("\n");
+               }
             }
-            //if(compiler.targetPlatform.is32Bits)
-               f.Printf(" -m32");
-            //else if(compiler.targetPlatform.is64Bits)
-            //   f.Printf(" -m64");
-            f.Printf(" $(FPIC)");
-            //f.Printf(" -fpack-struct");
-         }
-         if(warnings)
-         {
-            if(warnings == all)
-               f.Printf(" -Wall");
-            if(warnings == none)
-               f.Printf(" -w");
-         }
-         if(debug)
-            f.Printf(" -g");
-         if(profile)
-            f.Printf(" -pg");
-         if(options && options.linkerOptions && options.linkerOptions.count)
-         {
-            f.Printf(" \\\n\t -Wl");
-            for(s : options.linkerOptions)
-               f.Printf(",%s", s);
          }
 
-         if(options && options.preprocessorDefinitions)
-            OutputListOption(f, "D", options.preprocessorDefinitions, newLine, false);
-         if(config && config.options && config.options.preprocessorDefinitions)
-            OutputListOption(f, "D", config.options.preprocessorDefinitions, newLine, false);
-         // no if?
-         OutputListOption(f, gccCompiler ? "isystem " : "I", compiler.includeDirs, lineEach, true);
-         if(options && options.includeDirs)
-            OutputListOption(f, "I", options.includeDirs, lineEach, true);
-         if(config && config.options && config.options.includeDirs)
-            OutputListOption(f, "I", config.options.includeDirs, lineEach, true);
-         f.Printf("\n\n");
-
-         f.Printf("CECFLAGS =%s%s%s%s", defaultPreprocessor ? "" : " -cpp ", defaultPreprocessor ? "" : compiler.cppCommand, 
-               crossCompiling ? " -t " : "", crossCompiling ? (char*)compiler.targetPlatform : "");
-         f.Printf("\n\n");
-
-         f.Printf("ECFLAGS =");
-         if(memoryGuard)
-            f.Printf(" -memguard");
-         if(strictNameSpaces)
-            f.Printf(" -strictns");
-         if(noLineNumbers)
-            f.Printf(" -nolinenumbers");
-         {
-            char * s;
-            if((s = defaultNameSpace) && s[0])
-               f.Printf(" -defaultns %s", s);
-         }
-         f.Printf("\n\n");
-
-         if(targetType != staticLibrary)
-         {
-            f.Printf("OFLAGS = -m32"); // TARGET_TYPE is fixed in a Makefile, we don't want this. $(if TARGET_TYPE_STATIC_LIBRARY,,-m32)");
-            if(profile)
-               f.Printf(" -pg");
-            // no if? 
-            OutputListOption(f, "L", compiler.libraryDirs, lineEach, true);
-            if(options && options.libraryDirs)
-               OutputListOption(f, "L", options.libraryDirs, lineEach, true);
-            if(config && config.options && config.options.libraryDirs)
-               OutputListOption(f, "L", config.options.libraryDirs, lineEach, true);
-            f.Printf("\n\n");
-         }
-
-         f.Printf("LIBS =");
-         if(targetType != staticLibrary)
+         numObjects = topNode.GenMakefilePrintNode(f, this, objects, namesInfo, listItems, config, &containsCXX);
+         if(numObjects)
+            objectsParts = OutputFileList(f, "_OBJECTS", listItems, varStringLenDiffs, null);
+         f.Printf("OBJECTS =%s%s%s\n", numObjects ? " $(_OBJECTS)" : "", numCObjects ? " $(ECOBJECTS)" : "", numCObjects ? " $(OBJ)$(MODULE).main$(O)" : "");
+         f.Puts("\n");
+
+         topNode.GenMakefilePrintNode(f, this, sources, null, listItems, config, null);
+         OutputFileList(f, "SOURCES", listItems, varStringLenDiffs, numCObjects ? "$(ECSOURCES)" : null);
+
+         if(!noResources)
+            resNode.GenMakefilePrintNode(f, this, resources, null, listItems, config, null);
+         OutputFileList(f, "RESOURCES", listItems, varStringLenDiffs, null);
+
+         f.Puts("LIBS += $(SHAREDLIB) $(EXECUTABLE) $(LINKOPT)\n");
+         f.Puts("\n");
+         if((config && config.options && config.options.libraries) ||
+               (options && options.libraries))
          {
+            f.Puts("ifndef STATIC_LIBRARY_TARGET\n");
+            f.Puts("LIBS +=");
             if(config && config.options && config.options.libraries)
                OutputLibraries(f, config.options.libraries);
             else if(options && options.libraries)
                OutputLibraries(f, options.libraries);
+            f.Puts("\n");
+            f.Puts("endif\n");
+            f.Puts("\n");
          }
-         f.Printf(" $(SHAREDLIB) $(EXECUTABLE) $(LINKOPT)\n\n");
-
-         f.Printf("UPXFLAGS = -9\n\n"); // TOFEAT: Compression Level Option? Other UPX Options?
 
-         f.Printf("# HARD CODED PLATFORM-SPECIFIC OPTIONS\n");
-         f.Printf("ifdef %s\n", PlatformToMakefileVariable(tux));
-         f.Printf("OFLAGS += -Wl,--no-undefined\n");
-         f.Printf("endif\n\n");
+         topNode.GenMakeCollectAssignNodeFlags(config, numCObjects,
+               cflagsVariations, nodeCFlagsMapping,
+               ecflagsVariations, nodeECFlagsMapping, null);
 
-         // JF's
-         f.Printf("ifdef %s\n", PlatformToMakefileVariable(apple));
-         f.Printf("OFLAGS += -framework cocoa -framework OpenGL\n");
-         f.Printf("endif\n\n");
+         GenMakePrintCustomFlags(f, "CFLAGS", false, cflagsVariations);
+         GenMakePrintCustomFlags(f, "ECFLAGS", true, ecflagsVariations);
 
          if(platforms || (config && config.platforms))
          {
-            int ifCount = 0;
-            Platform platform;
+            ifCount = 0;
             //for(platform = firstPlatform; platform <= lastPlatform; platform++)
             //for(platform = win32; platform <= apple; platform++)
-            
-            f.Printf("# PLATFORM-SPECIFIC OPTIONS\n\n");
+
+            f.Puts("# PLATFORM-SPECIFIC OPTIONS\n\n");
             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
             {
-               PlatformOptions projectPlatformOptions = null;
-               PlatformOptions configPlatformOptions = null;
+               PlatformOptions projectPlatformOptions, configPlatformOptions;
+               MatchProjectAndConfigPlatformOptions(config, platform, &projectPlatformOptions, &configPlatformOptions);
 
-               if(platforms)
-               {
-                  for(p : platforms)
-                  {
-                     if(!strcmpi(p.name, platform))
-                     {
-                        projectPlatformOptions = p;
-                        break;
-                     }
-                  }
-               }
-               if(config && config.platforms)
-               {
-                  for(p : config.platforms)
-                  {
-                     if(!strcmpi(p.name, platform))
-                     {
-                        configPlatformOptions = p;
-                        break;
-                     }
-                  }
-               }
                if(projectPlatformOptions || configPlatformOptions)
                {
                   if(ifCount)
-                     f.Printf("else\n");
+                     f.Puts("else\n");
                   ifCount++;
-                  f.Printf("ifdef ");
-                  // we should have some kind of direct mapping between a platform and it's makefile variable
-                  f.Printf(PlatformToMakefileVariable(platform));
-                  f.Printf("\n\n");
-
-                  if((projectPlatformOptions && projectPlatformOptions.options.preprocessorDefinitions && projectPlatformOptions.options.preprocessorDefinitions.count) ||
-                     (configPlatformOptions && configPlatformOptions.options.preprocessorDefinitions && configPlatformOptions.options.preprocessorDefinitions.count) ||
-                     (projectPlatformOptions && projectPlatformOptions.options.includeDirs && projectPlatformOptions.options.includeDirs.count) ||
-                     (configPlatformOptions && configPlatformOptions.options.includeDirs && configPlatformOptions.options.includeDirs.count))
+                  f.Printf("ifdef %s\n", PlatformToMakefileTargetVariable(platform));
+                  f.Puts("\n");
+
+                  if((projectPlatformOptions && projectPlatformOptions.options.linkerOptions && projectPlatformOptions.options.linkerOptions.count) ||
+                     (configPlatformOptions && configPlatformOptions.options.linkerOptions && configPlatformOptions.options.linkerOptions.count))
                   {
-                     f.Printf("CFLAGS +=");
+                     f.Puts("CFLAGS +=");
+                     // tocheck: does any of that -Wl stuff from linkerOptions have any business being in CFLAGS?
                      if(projectPlatformOptions && projectPlatformOptions.options.linkerOptions && projectPlatformOptions.options.linkerOptions.count)
                      {
-                        f.Printf(" \\\n\t -Wl");
+                        f.Puts(" \\\n\t -Wl");
                         for(s : projectPlatformOptions.options.linkerOptions)
                            f.Printf(",%s", s);
                      }
                      if(configPlatformOptions && configPlatformOptions.options.linkerOptions && configPlatformOptions.options.linkerOptions.count)
                      {
-                        f.Printf(" \\\n\t -Wl");
+                        f.Puts(" \\\n\t -Wl");
                         for(s : configPlatformOptions.options.linkerOptions)
                            f.Printf(",%s", s);
                      }
-                     if(projectPlatformOptions && projectPlatformOptions.options.preprocessorDefinitions)
-                        OutputListOption(f, "D", projectPlatformOptions.options.preprocessorDefinitions, newLine, false);
-                     if(configPlatformOptions && configPlatformOptions.options.preprocessorDefinitions)
-                        OutputListOption(f, "D", configPlatformOptions.options.preprocessorDefinitions, newLine, false );
-                     if(projectPlatformOptions && projectPlatformOptions.options.includeDirs)
-                        OutputListOption(f, "I", projectPlatformOptions.options.includeDirs, lineEach, true);
-                     if(configPlatformOptions && configPlatformOptions.options.includeDirs)
-                        OutputListOption(f, "I", configPlatformOptions.options.includeDirs, lineEach, true);
-                     f.Printf("\n\n");
+                     f.Puts("\n");
+                     f.Puts("\n");
                   }
 
-                  if(targetType != staticLibrary)
+                  if((projectPlatformOptions && projectPlatformOptions.options.libraryDirs && projectPlatformOptions.options.libraryDirs.count) ||
+                        (configPlatformOptions && configPlatformOptions.options.libraryDirs && configPlatformOptions.options.libraryDirs.count) ||
+                        (projectPlatformOptions && projectPlatformOptions.options.libraries && projectPlatformOptions.options.libraries.count) ||
+                        (configPlatformOptions && configPlatformOptions.options.libraries && configPlatformOptions.options.libraries.count))
                   {
+                     f.Puts("ifndef STATIC_LIBRARY_TARGET\n");
                      if((projectPlatformOptions && projectPlatformOptions.options.libraryDirs && projectPlatformOptions.options.libraryDirs.count) ||
                         (configPlatformOptions && configPlatformOptions.options.libraryDirs && configPlatformOptions.options.libraryDirs.count))
                      {
-                        f.Printf("OFLAGS +=");
-                        if(projectPlatformOptions && projectPlatformOptions.options.libraryDirs)
-                           OutputListOption(f, "L", projectPlatformOptions.options.libraryDirs, lineEach, true);
+                        f.Puts("OFLAGS +=");
                         if(configPlatformOptions && configPlatformOptions.options.libraryDirs)
                            OutputListOption(f, "L", configPlatformOptions.options.libraryDirs, lineEach, true);
-                        f.Printf("\n\n");
+                        if(projectPlatformOptions && projectPlatformOptions.options.libraryDirs)
+                           OutputListOption(f, "L", projectPlatformOptions.options.libraryDirs, lineEach, true);
+                        f.Puts("\n");
                      }
 
-                     if(projectPlatformOptions && projectPlatformOptions.options.libraries &&
-                           projectPlatformOptions.options.libraries.count/* && 
-                           (!config || !config.options || !config.options.libraries)*/)
+                     if((configPlatformOptions && configPlatformOptions.options.libraries))
                      {
-                        f.Printf("LIBS +=");
-                        OutputLibraries(f, projectPlatformOptions.options.libraries);
-                        f.Printf("\n\n");
+                        if(configPlatformOptions.options.libraries.count)
+                        {
+                           f.Puts("LIBS +=");
+                           OutputLibraries(f, configPlatformOptions.options.libraries);
+                           f.Puts("\n");
+                        }
                      }
-                     {/*TOFIX: EXTRA CURLIES FOR NASTY WARNING*/if((configPlatformOptions && configPlatformOptions.options.libraries && configPlatformOptions.options.libraries.count))
+                     else if(projectPlatformOptions && projectPlatformOptions.options.libraries)
                      {
-                        f.Printf("LIBS +=");
-                        OutputLibraries(f, configPlatformOptions.options.libraries);
-                        f.Printf("\n\n");
-                     }}
+                        if(projectPlatformOptions.options.libraries.count)
+                        {
+                           f.Puts("LIBS +=");
+                           OutputLibraries(f, projectPlatformOptions.options.libraries);
+                           f.Puts("\n");
+                        }
+                     }
+                     f.Puts("endif\n");
+                     f.Puts("\n");
                   }
                }
             }
             if(ifCount)
             {
                for(c = 0; c < ifCount; c++)
-                  f.Printf("endif\n");
-               ifCount = 0;
+                  f.Puts("endif\n");
             }
-            f.Printf("\n");
+            f.Puts("\n");
          }
 
-         f.Printf("# TARGETS\n\n");
-
-         f.Printf("all: objdir%s $(TARGET)\n\n", sameObjTargetDirs ? "" : " targetdir");
-
-         f.Printf("objdir:\n");
-            f.Printf("\t$(if $(wildcard $(OBJ)),,$(call mkdirq,$(OBJ)))\n");
-         //f.Printf("# PRE-BUILD COMMANDS\n");
+         // tocheck: does any of that -Wl stuff from linkerOptions have any business being in CFLAGS?
+         if(options && options.linkerOptions && options.linkerOptions.count)
+         {
+            f.Puts("CFLAGS +=");
+            f.Puts(" \\\n\t -Wl");
+            for(s : options.linkerOptions)
+               f.Printf(",%s", s);
+         }
+         f.Puts("\n");
+         f.Puts("\n");
+
+         f.Puts("CECFLAGS += -cpp $(_CPP)");
+         f.Puts("\n");
+         f.Puts("\n");
+
+         f.Puts("ifndef STATIC_LIBRARY_TARGET\n");
+         f.Puts("OFLAGS +=");
+         forceBitDepth = (options && options.buildBitDepth) || numCObjects;
+         if(forceBitDepth)
+            f.Puts((!options || !options.buildBitDepth || options.buildBitDepth == bits32) ? " $(FORCE_32_BIT)" : " $(FORCE_64_BIT) \\\n");
+
+         if(GetProfile(config))
+            f.Puts(" -pg");
+         if(config && config.options && config.options.libraryDirs)
+            OutputListOption(f, "L", config.options.libraryDirs, lineEach, true);
+         if(options && options.libraryDirs)
+            OutputListOption(f, "L", options.libraryDirs, lineEach, true);
+         f.Puts("\n");
+         f.Puts("endif\n");
+         f.Puts("\n");
+
+         f.Puts("# TARGETS\n");
+         f.Puts("\n");
+
+         f.Printf("all: objdir%s $(TARGET)\n", sameObjTargetDirs ? "" : " targetdir");
+         f.Puts("\n");
+
+         f.Puts("objdir:\n");
+            f.Puts("\t$(if $(wildcard $(OBJ)),,$(call mkdirq,$(OBJ)))\n");
+         //f.Puts("# PRE-BUILD COMMANDS\n");
          if(options && options.prebuildCommands)
          {
             for(s : options.prebuildCommands)
@@ -2032,45 +2560,20 @@ private:
          }
          if(platforms || (config && config.platforms))
          {
-            int ifCount = 0;
-            Platform platform;
-            //f.Printf("# PLATFORM-SPECIFIC PRE-BUILD COMMANDS\n");
+            ifCount = 0;
+            //f.Puts("# TARGET_PLATFORM-SPECIFIC PRE-BUILD COMMANDS\n");
             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
             {
-               PlatformOptions projectPOs = null;
-               PlatformOptions configPOs = null;
+               PlatformOptions projectPOs, configPOs;
+               MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
 
-               if(platforms)
-               {
-                  for(p : platforms)
-                  {
-                     if(!strcmpi(p.name, platform))
-                     {
-                        projectPOs = p;
-                        break;
-                     }
-                  }
-               }
-               if(config && config.platforms)
-               {
-                  for(p : config.platforms)
-                  {
-                     if(!strcmpi(p.name, platform))
-                     {
-                        configPOs = p;
-                        break;
-                     }
-                  }
-               }
                if((projectPOs && projectPOs.options.prebuildCommands && projectPOs.options.prebuildCommands.count) ||
                      (configPOs && configPOs.options.prebuildCommands && configPOs.options.prebuildCommands.count))
                {
                   if(ifCount)
-                     f.Printf("else\n");
+                     f.Puts("else\n");
                   ifCount++;
-                  f.Printf("ifdef ");
-                  f.Printf(PlatformToMakefileVariable(platform));
-                  f.Printf("\n");
+                  f.Printf("ifdef %s\n", PlatformToMakefileTargetVariable(platform));
 
                   if(projectPOs && projectPOs.options.prebuildCommands && projectPOs.options.prebuildCommands.count)
                   {
@@ -2088,70 +2591,73 @@ private:
             {
                int c;
                for(c = 0; c < ifCount; c++)
-                  f.Printf("endif\n");
-               ifCount = 0;
+                  f.Puts("endif\n");
             }
          }
-         f.Printf("\n");
+         f.Puts("\n");
 
          if(!sameObjTargetDirs)
          {
-            f.Printf("targetdir:\n");
-               f.Printf("\t$(if $(wildcard %s),,$(call mkdirq,%s))\n\n", targetDirExpNoSpaces, targetDirExpNoSpaces);
+            f.Puts("targetdir:\n");
+               f.Printf("\t$(if $(wildcard %s),,$(call mkdirq,%s))\n", targetDirExpNoSpaces, targetDirExpNoSpaces);
+            f.Puts("\n");
          }
 
          if(numCObjects)
          {
             // Main Module (Linking) for ECERE C modules
-            f.Printf("$(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS)\n");
+            f.Puts("$(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS)\n");
             // use of objDirExpNoSpaces used instead of $(OBJ) to prevent problematic joining of arguments in ecs
-            f.Printf("\t$(ECS)%s $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols %s -o $(OBJ)$(MODULE).main.ec\n\n", 
-               console ? " -console" : "", objDirExpNoSpaces);
+            f.Printf("\t$(ECS)%s $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols %s -o $(OBJ)$(MODULE).main.ec\n", 
+               GetConsole(config) ? " -console" : "", objDirExpNoSpaces);
+            f.Puts("\n");
             // Main Module (Linking) for ECERE C modules
-            f.Printf("$(OBJ)$(MODULE).main.c: $(OBJ)$(MODULE).main.ec\n");
-            f.Printf("\t$(ECP) $(CECFLAGS) $(ECFLAGS) $(CFLAGS)"
+            f.Puts("$(OBJ)$(MODULE).main.c: $(OBJ)$(MODULE).main.ec\n");
+            f.Puts("\t$(ECP) $(CECFLAGS) $(ECFLAGS) $(CFLAGS)"
                   " -c $(OBJ)$(MODULE).main.ec -o $(OBJ)$(MODULE).main.sym -symbols $(OBJ)\n");
-            f.Printf("\t$(ECC) $(CECFLAGS) $(ECFLAGS) $(CFLAGS) $(FVISIBILITY)"
-                  " -c $(OBJ)$(MODULE).main.ec -o $(OBJ)$(MODULE).main.c -symbols $(OBJ)\n\n");
+            f.Puts("\t$(ECC) $(CECFLAGS) $(ECFLAGS) $(CFLAGS) $(FVISIBILITY)"
+                  " -c $(OBJ)$(MODULE).main.ec -o $(OBJ)$(MODULE).main.c -symbols $(OBJ)\n");
+            f.Puts("\n");
          }
 
-         // Target
+         // *** Target ***
+
+         // This would not rebuild the target on updated objects
          // f.Printf("$(TARGET): $(SOURCES) $(RESOURCES) | objdir $(SYMBOLS) $(OBJECTS)%s\n", sameObjTargetDirs ? "" : " targetdir");
+
+         // This should fix it for good!
+         f.Puts("$(SYMBOLS): | objdir\n");
+         f.Puts("$(OBJECTS): | objdir\n");
+
+         // This alone was breaking the tarball, object directory does not get created first (order-only rules happen last it seems!)
          f.Printf("$(TARGET): $(SOURCES) $(RESOURCES) $(SYMBOLS) $(OBJECTS) | objdir%s\n", sameObjTargetDirs ? "" : " targetdir");
-         if(targetType == sharedLibrary || targetType == executable)
-         {
-            // f.Printf("\tinstall_name_tool $(TARGET) $(LP)$(MODULE)%s\n", targetType == sharedLibrary ? "$(SO)" : "$(A)");
-            //f.Printf("ifdef OSX\n");
-            //f.Printf("ifeq \"$(TARGET_TYPE)\" \"sharedlib\"\n");
-            //f.Printf("\t$(CC) $(OFLAGS) $(OBJECTS) $(LIBS) -o $(TARGET) -install_name $(LP)$(MODULE)$(SO)\n");
-            //f.Printf("endif\n");
-            //f.Printf("else\n");
-            //f.Printf("\t$(CC) $(OFLAGS) $(OBJECTS) $(LIBS) -o $(TARGET)\n");
-            //f.Printf("endif\n");
-            f.Printf("\t$(CC) $(OFLAGS) $(OBJECTS) $(LIBS) -o $(TARGET) $(INSTALLNAME)\n");
-
-            if(!debug)
-            {
-               f.Printf("\t$(STRIP) $(STRIPOPT) $(TARGET)\n");
 
-               if(compress)
-               {
-                  f.Printf("ifndef WINDOWS\n");
-                  f.Printf("ifeq \"$(TARGET_TYPE)\" \"executable\"\n");
-                     f.Printf("\t$(UPX) $(UPXFLAGS) $(TARGET)\n");
-                  f.Printf("endif\n");
-                  f.Printf("else\n");
-                     f.Printf("\t$(UPX) $(UPXFLAGS) $(TARGET)\n");
-                  f.Printf("endif\n");
-               }
+         f.Puts("ifndef STATIC_LIBRARY_TARGET\n");
+         f.Printf("\t$(%s) $(OFLAGS) $(OBJECTS) $(LIBS) %s-o $(TARGET) $(INSTALLNAME)\n", containsCXX ? "CXX" : "CC", containsCXX ? "-lstdc++ " : "");
+         if(!GetDebug(config))
+         {
+            f.Puts("ifndef NOSTRIP\n");
+            f.Puts("\t$(STRIP) $(STRIPOPT) $(TARGET)\n");
+            f.Puts("endif\n");
+
+            if(GetCompress(config))
+            {
+               f.Printf("ifndef %s\n", PlatformToMakefileTargetVariable(win32));
+               f.Puts("ifdef EXECUTABLE_TARGET\n");
+                  f.Puts("\t$(UPX) $(UPXFLAGS) $(TARGET)\n");
+               f.Puts("endif\n");
+               f.Puts("else\n");
+                  f.Puts("\t$(UPX) $(UPXFLAGS) $(TARGET)\n");
+               f.Puts("endif\n");
             }
-            if(resNode.files && resNode.files.count && !noResources)
-               resNode.GenMakefileAddResources(f, resNode.path);
          }
-         else
-            f.Printf("\t$(AR) rcs $(TARGET) $(OBJECTS) $(LIBS)\n");
+         if(resNode.files && resNode.files.count && !noResources)
+            resNode.GenMakefileAddResources(f, resNode.path, config);
+         f.Puts("else\n");
+         f.Puts("\t$(AR) rcs $(TARGET) $(OBJECTS) $(LIBS)\n");
+         f.Puts("endif\n");
 
-         //f.Printf("# POST-BUILD COMMANDS\n");
+         //f.Puts("# POST-BUILD COMMANDS\n");
          if(options && options.postbuildCommands)
          {
             for(s : options.postbuildCommands)
@@ -2164,46 +2670,20 @@ private:
          }
          if(platforms || (config && config.platforms))
          {
-            int ifCount = 0;
-            Platform platform;
-
-            //f.Printf("# PLATFORM-SPECIFIC POST-BUILD COMMANDS\n");
+            ifCount = 0;
+            //f.Puts("# TARGET_PLATFORM-SPECIFIC POST-BUILD COMMANDS\n");
             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
             {
-               PlatformOptions projectPOs = null;
-               PlatformOptions configPOs = null;
+               PlatformOptions projectPOs, configPOs;
+               MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
 
-               if(platforms)
-               {
-                  for(p : platforms)
-                  {
-                     if(!strcmpi(p.name, platform))
-                     {
-                        projectPOs = p;
-                        break;
-                     }
-                  }
-               }
-               if(config && config.platforms)
-               {
-                  for(p : config.platforms)
-                  {
-                     if(!strcmpi(p.name, platform))
-                     {
-                        configPOs = p;
-                        break;
-                     }
-                  }
-               }
                if((projectPOs && projectPOs.options.postbuildCommands && projectPOs.options.postbuildCommands.count) ||
                      (configPOs && configPOs.options.postbuildCommands && configPOs.options.postbuildCommands.count))
                {
                   if(ifCount)
-                     f.Printf("else\n");
+                     f.Puts("else\n");
                   ifCount++;
-                  f.Printf("ifdef ");
-                  f.Printf(PlatformToMakefileVariable(platform));
-                  f.Printf("\n");
+                  f.Printf("ifdef %s\n", PlatformToMakefileTargetVariable(platform));
 
                   if(projectPOs && projectPOs.options.postbuildCommands && projectPOs.options.postbuildCommands.count)
                   {
@@ -2221,52 +2701,67 @@ private:
             {
                int c;
                for(c = 0; c < ifCount; c++)
-                  f.Printf("endif\n");
-               ifCount = 0;
+                  f.Puts("endif\n");
             }
          }
-         f.Printf("\n");
-
-         f.Printf("# SYMBOL RULES\n\n");
-         topNode.GenMakefilePrintSymbolRules(f, this);
+         f.Puts("\n");
 
-         f.Printf("# C OBJECT RULES\n\n");
-         topNode.GenMakefilePrintCObjectRules(f, this);
-
-         /*if(numCObjects)
+         f.Puts("# SYMBOL RULES\n");
+         f.Puts("\n");
          {
-            f.Printf("# IMPLICIT OBJECT RULE\n\n");
+            Map<Platform, bool> excludedPlatforms { };
+            topNode.GenMakefilePrintSymbolRules(f, this, config, excludedPlatforms,
+                  nodeCFlagsMapping, nodeECFlagsMapping);
+            delete excludedPlatforms;
+         }
 
-            f.Printf("$(OBJ)%%$(O) : $(OBJ)%%.c\n");
-            f.Printf("\t$(CC) $(CFLAGS) $(FVISIBILITY) -c $< -o $@\n\n");
-         }*/
+         f.Puts("# C OBJECT RULES\n");
+         f.Puts("\n");
+         {
+            Map<Platform, bool> excludedPlatforms { };
+            topNode.GenMakefilePrintCObjectRules(f, this, config, excludedPlatforms,
+                  nodeCFlagsMapping, nodeECFlagsMapping);
+            delete excludedPlatforms;
+         }
 
-         f.Printf("# OBJECT RULES\n\n");
+         f.Puts("# OBJECT RULES\n");
+         f.Puts("\n");
          // todo call this still but only generate rules whith specific options
          // see we-have-file-specific-options in ProjectNode.ec
-         topNode.GenMakefilePrintObjectRules(f, this, namesInfo);
+         {
+            Map<Platform, bool> excludedPlatforms { };
+            topNode.GenMakefilePrintObjectRules(f, this, namesInfo, config, excludedPlatforms,
+                  nodeCFlagsMapping, nodeECFlagsMapping);
+            delete excludedPlatforms;
+         }
 
          if(numCObjects)
-            GenMakefilePrintMainObjectRule(f);
+            GenMakefilePrintMainObjectRule(f, config);
 
          f.Printf("clean: objdir%s\n", sameObjTargetDirs ? "" : " targetdir");
-         f.Printf("\t$(call rmq,%s$(TARGET))\n", numCObjects ? "$(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S) " : "");
-         OutputCleanActions(f, "OBJECTS", objectsParts);
-         OutputCleanActions(f, "COBJECTS", cobjectsParts);
+         f.Printf("\t$(call rmq,%s$(TARGET))\n", numCObjects ? "$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S) " : "");
+         OutputCleanActions(f, "_OBJECTS", objectsParts);
          if(numCObjects)
          {
-            OutputCleanActions(f, "IMPORTS", importsParts);
-            OutputCleanActions(f, "SYMBOLS", symbolsParts);
+            OutputCleanActions(f, "ECOBJECTS", eCsourcesParts);
+            OutputCleanActions(f, "COBJECTS", eCsourcesParts);
+            OutputCleanActions(f, "BOWLS", eCsourcesParts);
+            OutputCleanActions(f, "IMPORTS", eCsourcesParts);
+            OutputCleanActions(f, "SYMBOLS", eCsourcesParts);
          }
-         f.Printf("\n");
+         f.Puts("\n");
 
-         f.Printf("distclean: clean\n");
-         f.Printf("\t$(call rmdirq,$(OBJ))\n");
+         f.Puts("realclean: clean\n");
+         f.Puts("\t$(call rmrq,$(OBJ))\n");
          if(!sameObjTargetDirs)
             f.Printf("\t$(call rmdirq,%s)\n", targetDirExpNoSpaces);
+         f.Puts("\n");
+
+         f.Puts("distclean:\n");
+         f.Puts("\t$(call rmrq,obj/)\n");
 
          delete f;
-         delete objDirExp;
+
          listItems.Free();
          delete listItems;
          varStringLenDiffs.Free();
@@ -2274,21 +2769,23 @@ private:
          namesInfo.Free();
          delete namesInfo;
 
+         delete cflagsVariations;
+         delete nodeCFlagsMapping;
+         delete ecflagsVariations;
+         delete nodeECFlagsMapping;
+
          result = true;
       }
 
-      /*ChangeWorkingDir(oldDirectory);
-      SetEnvironment("PATH", oldPath);*/
+      /ChangeWorkingDir(oldDirectory);
+      // delete pathBackup;
 
       if(config)
          config.makingModified = false;
-
-      delete compiler;
-
       return result;
    }
 
-   void GenMakefilePrintMainObjectRule(File f)
+   void GenMakefilePrintMainObjectRule(File f, ProjectConfig config)
    {
       char extension[MAX_EXTENSION] = "c";
       char modulePath[MAX_LOCATION];
@@ -2296,9 +2793,9 @@ private:
       DualPipe dep;
       char command[2048];
       char objDirNoSpaces[MAX_LOCATION];
-      DirExpression objDirExp = objDir;
+      String objDirExp = GetObjDirExpression(config);
 
-      ReplaceSpaces(objDirNoSpaces, objDirExp.dir);
+      ReplaceSpaces(objDirNoSpaces, objDirExp);
       ReplaceSpaces(fixedModuleName, moduleName);
       
       //sprintf(fixedModuleName, "%s.main", fixedPrjName);
@@ -2373,15 +2870,66 @@ private:
          if(!result)
          {
 #endif
-            f.Printf("$(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c\n");
+            f.Puts("$(OBJ)$(MODULE).main$(O): $(OBJ)$(MODULE).main.c\n");
 #if 0
          }
       }
 #endif
 
-      f.Printf("\t$(CC) $(CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.%s -o $(OBJ)$(MODULE).main$(O)\n\n", extension);
+      f.Printf("\t$(CC) $(CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.%s -o $(OBJ)$(MODULE).main$(O)\n", extension);
+      f.Puts("\n");
+   }
 
-      delete objDirExp;
+   void GenMakePrintCustomFlags(File f, String variableName, bool printNonCustom, Map<String, int> cflagsVariations)
+   {
+      int c;
+      for(c = printNonCustom ? 0 : 1; c <= cflagsVariations.count; c++)
+      {
+         for(v : cflagsVariations)
+         {
+            if(v == c)
+            {
+               if(v == 1)
+                  f.Printf("%s +=", variableName);
+               else
+                  f.Printf("CUSTOM%d_%s =", v-1, variableName);
+               f.Puts(&v ? &v : "");
+               f.Puts("\n");
+               f.Puts("\n");
+               break;
+            }
+         }
+      }
+      f.Puts("\n");
+   }
+
+   void MatchProjectAndConfigPlatformOptions(ProjectConfig config, Platform platform,
+         PlatformOptions * projectPlatformOptions, PlatformOptions * configPlatformOptions)
+   {
+      *projectPlatformOptions = null;
+      *configPlatformOptions = null;
+      if(platforms)
+      {
+         for(p : platforms)
+         {
+            if(!strcmpi(p.name, platform))
+            {
+               *projectPlatformOptions = p;
+               break;
+            }
+         }
+      }
+      if(config && config.platforms)
+      {
+         for(p : config.platforms)
+         {
+            if(!strcmpi(p.name, platform))
+            {
+               *configPlatformOptions = p;
+               break;
+            }
+         }
+      }
    }
 }
 
@@ -2430,6 +2978,7 @@ Project LegacyBinaryLoadProject(File f, char * filePath)
       {
          int temp;
          int len,c, count;
+         String targetFileName, targetDirectory, objectsDirectory;
 
          // { executable = 0, sharedLibrary = 1, staticLibrary = 2 };
          f.Read(&temp, sizeof(int),1);
@@ -2441,18 +2990,22 @@ Project LegacyBinaryLoadProject(File f, char * filePath)
          }
 
          f.Read(&len, sizeof(int),1);
-         project.options.targetFileName = new char[len+1];
-         f.Read(project.options.targetFileName, sizeof(char), len+1);
+         targetFileName = new char[len+1];
+         f.Read(targetFileName, sizeof(char), len+1);
+         project.options.targetFileName = targetFileName;
+         delete targetFileName;
 
          f.Read(&len, sizeof(int),1);
-         delete project.options.targetDirectory;
-         project.options.targetDirectory = new char[len+1];
-         f.Read(project.options.targetDirectory, sizeof(char), len+1);
+         targetDirectory = new char[len+1];
+         f.Read(targetDirectory, sizeof(char), len+1);
+         project.options.targetDirectory = targetDirectory;
+         delete targetDirectory;
 
          f.Read(&len, sizeof(int),1);
-         delete project.options.objectsDirectory;
-         project.options.objectsDirectory = new byte[len+1];
-         f.Read(project.options.objectsDirectory, sizeof(char), len+1);
+         objectsDirectory = new byte[len+1];
+         f.Read(objectsDirectory, sizeof(char), len+1);
+         project.options.objectsDirectory = objectsDirectory;
+         delete objectsDirectory;
 
          f.Read(&temp, sizeof(int),1);
          project./*config.*/options.debug = temp ? true : false;
@@ -2614,7 +3167,7 @@ void ProjectConfig::LegacyProjectConfigLoad(File f)
                equal++;
                TrimLSpaces(equal, equal);
                if(!strcmpi(buffer, "Target Name"))
-                  options.targetFileName = CopyString(equal);
+                  options.targetFileName = /*CopyString(*/equal/*)*/;
                else if(!strcmpi(buffer, "Target Type"))
                {
                   if(!strcmpi(equal, "Executable"))
@@ -2627,7 +3180,7 @@ void ProjectConfig::LegacyProjectConfigLoad(File f)
                      options.targetType = executable;
                }
                else if(!strcmpi(buffer, "Target Directory"))
-                  options.targetDirectory = CopyString(equal);
+                  options.targetDirectory = /*CopyString(*/equal/*)*/;
                else if(!strcmpi(buffer, "Console"))
                   options.console = ParseTrueFalseValue(equal);
                else if(!strcmpi(buffer, "Libraries"))
@@ -2636,7 +3189,7 @@ void ProjectConfig::LegacyProjectConfigLoad(File f)
                   ParseArrayValue(options.libraries, equal);
                }
                else if(!strcmpi(buffer, "Intermediate Directory"))
-                  options.objectsDirectory = CopyString(equal); //objDir.expression = equal;
+                  options.objectsDirectory = /*CopyString(*/equal/*)*/; //objDir.expression = equal;
                else if(!strcmpi(buffer, "Debug"))
                   options.debug = ParseTrueFalseValue(equal);
                else if(!strcmpi(buffer, "Optimize"))
@@ -2672,7 +3225,7 @@ void ProjectConfig::LegacyProjectConfigLoad(File f)
       }
    }
    if(!options.targetDirectory && options.objectsDirectory)
-      options.targetDirectory = CopyString(options.objectsDirectory);
+      options.targetDirectory = /*CopyString(*/options.objectsDirectory/*)*/;
    //if(!objDir.dir) objDir.dir = "obj";
    //if(!targetDir.dir) targetDir.dir = "";
    // if(!targetName) property::targetName = "";   // How can a targetFileName be nothing???
@@ -2712,13 +3265,17 @@ Project LegacyAsciiLoadProject(File f, char * filePath)
             TrimLSpaces(equal, equal);
             if(!strcmpi(section, "Target") && !strcmpi(subSection, "LibraryDirs"))
             {
-               if(!project.config.options.libraryDirs) project.config.options.libraryDirs = { };
-               project.config.options.libraryDirs.Add(CopyString(equal));
+               if(!project.config.options.libraryDirs)
+                  project.config.options.libraryDirs = { [ CopyString(equal) ] };
+               else
+                  project.config.options.libraryDirs.Add(CopyString(equal));
             }
             else if(!strcmpi(section, "Target") && !strcmpi(subSection, "IncludeDirs"))
             {
-               if(!project.config.options.includeDirs) project.config.options.includeDirs = { };
-               project.config.options.includeDirs.Add(CopyString(equal));
+               if(!project.config.options.includeDirs)
+                  project.config.options.includeDirs = { [ CopyString(equal) ] };
+               else
+                  project.config.options.includeDirs.Add(CopyString(equal));
             }
             else if(!strcmpi(section, "Target") && (!strcmpi(subSection, "Files") || !strcmpi(subSection, "Resources")))
             {
@@ -2894,7 +3451,7 @@ Project LegacyAsciiLoadProject(File f, char * filePath)
 
                   // Config Settings
                   else if(!strcmpi(buffer, "Intermediate Directory"))
-                     project.config.options.objectsDirectory = CopyString(equal); //objDir.expression = equal;
+                     project.config.options.objectsDirectory = /*CopyString(*/equal/*)*/; //objDir.expression = equal;
                   else if(!strcmpi(buffer, "Debug"))
                      project.config.options.debug = ParseTrueFalseValue(equal);
                   else if(!strcmpi(buffer, "Optimize"))
@@ -2918,7 +3475,7 @@ Project LegacyAsciiLoadProject(File f, char * filePath)
 
                      // Project Wide Settings (All configs)
                      if(!strcmpi(buffer, "Target Name"))
-                        project.options.targetFileName = CopyString(equal);
+                        project.options.targetFileName = /*CopyString(*/equal/*)*/;
                      else if(!strcmpi(buffer, "Target Type"))
                      {
                         if(!strcmpi(equal, "Executable"))
@@ -2931,7 +3488,7 @@ Project LegacyAsciiLoadProject(File f, char * filePath)
                            project.options.targetType = executable;
                      }
                      else if(!strcmpi(buffer, "Target Directory"))
-                        project.options.targetDirectory = CopyString(equal);
+                        project.options.targetDirectory = /*CopyString(*/equal/*)*/;
                      else if(!strcmpi(buffer, "Console"))
                         project.options.console = ParseTrueFalseValue(equal);
                      else if(!strcmpi(buffer, "Libraries"))
@@ -3191,7 +3748,7 @@ void CombineIdenticalConfigOptions(Project project)
    }
 }
 
-Project LoadProject(char * filePath)
+Project LoadProject(char * filePath, char * activeConfigName)
 {
    Project project = null;
    File f = FileOpen(filePath, read);
@@ -3240,6 +3797,17 @@ Project LoadProject(char * filePath)
       if(project)
       {
          if(!project.options) project.options = { };
+         if(activeConfigName && activeConfigName[0] && project.configurations)
+         {
+            for(cfg : project.configurations)
+            {
+               if(!strcmpi(cfg.name, activeConfigName))
+               {
+                  project.config = cfg;
+                  break;
+               }
+            }
+         }
          if(!project.config && project.configurations)
             project.config = project.configurations.firstIterator.data;
 
@@ -3254,9 +3822,9 @@ Project LoadProject(char * filePath)
             (!project.options || !project.options.targetFileName || !project.options.targetFileName[0]) &&
             (!project.config.options.targetFileName || !project.config.options.targetFileName[0]))
          {
-            delete project.config.options.targetFileName;
+            //delete project.config.options.targetFileName;
             
-            project.options.targetFileName = CopyString(project.moduleName);
+            project.options.targetFileName = /*CopyString(*/project.moduleName/*)*/;
             project.config.options.optimization = none;
             project.config.options.debug = true;
             //project.config.options.warnings = unset;
@@ -3269,9 +3837,10 @@ Project LoadProject(char * filePath)
             project.topNode.name = CopyString(project.config.options.targetFileName);
          }
 
+         /* // THIS IS NOW AUTOMATED WITH A project CHECK IN ProjectNode
          project.topNode.configurations = project.configurations;
          project.topNode.platforms = project.platforms;
-         project.topNode.options = project.options;
+         project.topNode.options = project.options;*/
       }
    }
    return project;