ide/sdk: Fixed forcing bit depth option not invoked for static libraries
[sdk] / ide / src / project / Project.ec
index 7557192..e65519d 100644 (file)
@@ -4,6 +4,8 @@ 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
@@ -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
    }
 };
@@ -372,13 +373,45 @@ define PEEK_RESOLUTION = (18.2 * 10);
 #define SEPS    "/"
 #define SEP     '/'
 
+static Array<String> notLinkerOptions
+{ [
+   "-static-libgcc",
+   "-shared",
+   "-static",
+   "-s",
+   "-shared-libgcc",
+   "-nostartfiles",
+   "-nodefaultlibs",
+   "-nostdlib",
+   "-pie",
+   "-rdynamic",
+   "-static-libasan",
+   "-static-libtsan",
+   "-static libstdc++",
+   "-symbolic",
+   "-Wl,"
+   //"-T ",
+   //"-Xlinker ",
+   //"-u "
+] };
+
+static bool IsLinkerOption(String s)
+{
+   for(i : notLinkerOptions)
+      if(strstr(s, "-Wl,") || !strcmp(s, i))
+         return false;
+   return true;
+}
+
 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 +432,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,7 +464,7 @@ 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;
@@ -448,13 +503,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)
    {
@@ -487,14 +542,14 @@ int OutputFileList(File f, char * name, Array<String> list, Map<String, int> var
                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;
 }
@@ -516,11 +571,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);
@@ -530,6 +585,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)
@@ -537,9 +600,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"))
@@ -551,9 +615,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(")");
    }
 }
 
@@ -598,6 +665,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;
@@ -634,13 +704,12 @@ define platformTargetType =
                projectPOs.options.targetType : TargetTypes::unset;
 
 
-// we should have some kind of direct mapping between a platform and it's makefile variable
-char * PlatformToMakefileVariable(Platform platform)
+char * PlatformToMakefileTargetVariable(Platform platform)
 {
-   return platform == win32 ? "WINDOWS" :
-          platform == tux   ? "LINUX"   :
-          platform == apple ? "OSX"/*"APPLE"*/ :
-                              platform;
+   return platform == win32 ? "WINDOWS_TARGET" :
+          platform == tux   ? "LINUX_TARGET"   :
+          platform == apple ? "OSX_TARGET"     :
+                              "ERROR_BAD_TARGET";
 }
 
 char * TargetTypeToMakefileVariable(TargetTypes targetType)
@@ -657,6 +726,8 @@ char * GetConfigName(ProjectConfig config)
    return config ? config.name : "Common";
 }
 
+public enum SingleFileCompileMode { normal, debugPrecompile, debugCompile, debugGenerateSymbols };
+
 class Project : struct
 {
    class_no_expansion;  // To use Find on the Container<Project> in Workspace::projects
@@ -715,6 +786,13 @@ public:
       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;
@@ -730,15 +808,80 @@ private:
 
    String description;
    String license;
+   String compilerConfigsDir;
+#ifndef MAKEFILE_GENERATOR
+   FileMonitor fileMonitor
+   {
+      this, FileChange { modified = true };
+      bool OnFileNotify(FileChange action, char * param)
+      {
+         fileMonitor.StopMonitoring();
+         if(OnProjectModified(action, param))
+            fileMonitor.StartMonitoring();
+         return true;
+      }
+   };
 
-   ~Project()
+   bool StartMonitoring()
    {
-      /* // THIS IS NOW AUTOMATED WITH A project CHECK IN ProjectNode
-      topNode.configurations = null;
-      topNode.platforms = null;
-      topNode.options = null;
-      */
+      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; }
@@ -748,12 +891,23 @@ private:
 
       delete description;
       delete license;
+      delete compilerConfigsDir;
       delete moduleName;
       delete filePath;
       delete topNode;
       delete name;
    }
 
+   ~Project()
+   {
+      /* // THIS IS NOW AUTOMATED WITH A project CHECK IN ProjectNode
+      topNode.configurations = null;
+      topNode.platforms = null;
+      topNode.options = null;
+      */
+      Free();
+   }
+
    property ProjectConfig config
    {
       set
@@ -805,7 +959,7 @@ private:
    }
 
 
-   char * GetObjDirExpression(CompilerConfig compiler, ProjectConfig config)
+   char * GetObjDirExpression(ProjectConfig config)
    {
       // TODO: Support platform options
       char * expression = localObjectsDirectory;
@@ -816,13 +970,13 @@ private:
 
    DirExpression GetObjDir(CompilerConfig compiler, ProjectConfig config)
    {
-      char * expression = GetObjDirExpression(compiler, config);
+      char * expression = GetObjDirExpression(config);
       DirExpression objDir { type = intermediateObjectsDir };
       objDir.Evaluate(expression, this, compiler, config);
       return objDir;
    }
 
-   char * GetTargetDirExpression(CompilerConfig compiler, ProjectConfig config)
+   char * GetTargetDirExpression(ProjectConfig config)
    {
       // TODO: Support platform options
       char * expression = localTargetDirectory;
@@ -833,7 +987,7 @@ private:
 
    DirExpression GetTargetDir(CompilerConfig compiler, ProjectConfig config)
    {
-      char * expression = GetTargetDirExpression(compiler, config);
+      char * expression = GetTargetDirExpression(config);
       DirExpression targetDir { type = DirExpressionType::targetDir /*intermediateObjectsDir*/};
       targetDir.Evaluate(expression, this, compiler, config);
       return targetDir;
@@ -875,6 +1029,12 @@ private:
       return optimization;
    }
 
+   bool GetFastMath(ProjectConfig config)
+   {
+      SetBool fastMath = localFastMath;
+      return fastMath == true;
+   }
+
    String GetDefaultNameSpace(ProjectConfig config)
    {
       String defaultNameSpace = localDefaultNameSpace;
@@ -918,7 +1078,7 @@ private:
    bool GetConfigIsInDebugSession(ProjectConfig config)
    {
 #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
    }
 
@@ -959,6 +1119,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)
    {
@@ -972,6 +1133,44 @@ 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;   
    }
@@ -1014,14 +1213,76 @@ private:
       }
    }
 
-   void CatMakeFileName(char * string, CompilerConfig compiler, ProjectConfig config)
+   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 && 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];
       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 : "");
+      sprintf(string, "%s%s%s.Makefile", projectName, config ? "-" : "", config ? config.name : "");
    }
 
 #ifndef MAKEFILE_GENERATOR
@@ -1081,6 +1342,7 @@ private:
          }
 
          property::config = cfg.data;
+         ide.UpdateToolBarActiveConfigs(true);
          ide.workspace.modified = true;
          ide.projectView.Update(null);
       }
@@ -1120,7 +1382,7 @@ private:
       }
    }
 
-   bool ProcessBuildPipeOutput(DualPipe f, DirExpression objDirExp, bool isARun, ProjectNode onlyNode,
+   bool ProcessBuildPipeOutput(DualPipe f, DirExpression objDirExp, bool isARun, List<ProjectNode> onlyNodes,
       CompilerConfig compiler, ProjectConfig config)
    {
       char line[65536];
@@ -1128,32 +1390,51 @@ private:
       int compilingEC = 0;
       int numErrors = 0, numWarnings = 0;
       bool loggedALine = false;
-      char * configName = config.name;
+      char * configName = config ? config.name : "Common";
       int lenMakeCommand = strlen(compiler.makeCommand);
-      
-      char cppCommand[MAX_LOCATION];
-      char ccCommand[MAX_LOCATION];
-      char ecpCommand[MAX_LOCATION];
-      char eccCommand[MAX_LOCATION];
-      char ecsCommand[MAX_LOCATION];
-      char earCommand[MAX_LOCATION];
-
-      char * cc = compiler.ccCommand;
-      char * cpp = compiler.cppCommand;
-      sprintf(cppCommand, "%s%s%s%s ",
-            compiler.ccacheEnabled ? "ccache " : "",
-            compiler.ccacheEnabled && !compiler.distccEnabled ? " " : "",
-            compiler.distccEnabled ? "distcc " : "",
-            compiler.cppCommand);
-      sprintf(ccCommand, "%s%s%s%s ",
+      int testLen = 0;
+
+      char * gccPrefix = compiler.gccPrefix ? compiler.gccPrefix : "";
+
+      DynamicString test { };
+      DynamicString ecp { };
+      DynamicString ecc { };
+      DynamicString ecs { };
+      DynamicString ear { };
+      DynamicString prefix { };
+      DynamicString cpp { };
+      DynamicString cc { };
+      DynamicString cxx { };
+      DynamicString strip { };
+      DynamicString ar { };
+
+      ecp.concatx(compiler.ecpCommand, " ");
+      ecc.concatx(compiler.eccCommand, " ");
+      ecs.concatx(compiler.ecsCommand, " ");
+      ear.concatx(compiler.earCommand, " ");
+
+      prefix.concatx(
             compiler.ccacheEnabled ? "ccache " : "",
-            compiler.ccacheEnabled && !compiler.distccEnabled ? " " : "",
             compiler.distccEnabled ? "distcc " : "",
-            compiler.ccCommand);
-      sprintf(ecpCommand, "%s ", compiler.ecpCommand);
-      sprintf(eccCommand, "%s ", compiler.eccCommand);
-      sprintf(ecsCommand, "%s ", compiler.ecsCommand);
-      sprintf(earCommand, "%s ", compiler.earCommand);
+            gccPrefix);
+
+      cpp.concatx((String)prefix, compiler.cppCommand, " ");
+      cc.concatx((String)prefix, compiler.ccCommand,  " ");
+      cxx.concatx((String)prefix, compiler.cxxCommand, " ");
+
+      strip.concatx(gccPrefix, "strip ");
+      ar.concatx(gccPrefix, "ar rcs");
+
+      testLen = Max(testLen, ecp.size);
+      testLen = Max(testLen, ecc.size);
+      testLen = Max(testLen, ecs.size);
+      testLen = Max(testLen, ear.size);
+      testLen = Max(testLen, cpp.size);
+      testLen = Max(testLen, cc.size);
+      testLen = Max(testLen, cxx.size);
+      testLen = Max(testLen, strip.size);
+      testLen = Max(testLen, ar.size);
+      testLen++;
 
       while(!f.Eof() && !ide.ShouldStopBuild())
       {
@@ -1165,6 +1446,8 @@ private:
             //printf("Peeking and GetLine...\n");
             if((result = f.Peek()) && (result = f.GetLine(line, sizeof(line)-1)))
             {
+               char * inFileIncludedFrom = strstr(line, stringInFileIncludedFrom);
+               test.copyLenSingleBlankReplTrim(line, ' ', true, testLen);
                if(strstr(line, compiler.makeCommand) == line && line[lenMakeCommand] == ':')
                {
                   char * module = strstr(line, "No rule to make target `");
@@ -1178,30 +1461,30 @@ private:
                         *end = '\0';
                         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(test, ear) == test);
+               else if(strstr(test, strip) == test);
+               else if(strstr(test, cc) == test || strstr(test, cxx) == test || strstr(test, ecp) == test || strstr(test, ecc) == test)
                {
                   char moduleName[MAX_FILENAME];
                   byte * tokens[1];
                   char * module;
                   bool isPrecomp = false;
-               
-                  if(strstr(line, ccCommand) == line)
+
+                  if(strstr(test, cc) == test || strstr(test, cxx) == test)
                   {
                      module = strstr(line, " -c ");
                      if(module) module += 4;
                   }
-                  else if(strstr(line, eccCommand) == line)
+                  else if(strstr(test, ecc) == test)
                   {
                      module = strstr(line, " -c ");
                      if(module) module += 4;
@@ -1209,7 +1492,7 @@ private:
                      // Don't show GCC warnings about generated C code because it does not compile clean yet...
                      compilingEC = 3;//2;
                   }
-                  else if(strstr(line, ecpCommand) == line)
+                  else if(strstr(test, ecp) == test)
                   {
                      // module = line + 8;
                      module = strstr(line, " -c ");
@@ -1247,14 +1530,14 @@ private:
                   else
                   {
                      ide.outputView.buildBox.Logf("%s\n", line);
-                     numErrors ++;
+                     numErrors++;
                   }
 
                   if(compilingEC) compilingEC--;
                }
-               else if(strstr(line, "ar rcs") == line)
+               else if(strstr(test, ar) == test)
                   ide.outputView.buildBox.Logf($"Building library...\n");
-               else if(strstr(line, ecsCommand) == line)
+               else if(strstr(test, ecs) == test)
                   ide.outputView.buildBox.Logf($"Writing symbol loader...\n");
                else
                {
@@ -1267,22 +1550,24 @@ 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")))
                         {
@@ -1337,7 +1622,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
@@ -1379,7 +1664,7 @@ private:
          }
          else
          {
-            if(!onlyNode)
+            if(!onlyNodes)
                ide.outputView.buildBox.Logf("\n%s (%s) - ", GetTargetFileName(config), configName);
             if(numErrors)
                ide.outputView.buildBox.Logf("%d %s, ", numErrors, (numErrors > 1) ? $"errors" : $"error");
@@ -1392,6 +1677,19 @@ private:
                ide.outputView.buildBox.Logf($"no warning\n");
          }
       }
+
+      delete test;
+      delete ecp;
+      delete ecc;
+      delete ecs;
+      delete ear;
+      delete prefix;
+      delete cpp;
+      delete cc;
+      delete cxx;
+      delete strip;
+      delete ar;
+
       return numErrors == 0;
    }
 
@@ -1430,32 +1728,38 @@ private:
       }
    }
 
-   bool Build(bool isARun, ProjectNode onlyNode, CompilerConfig compiler, ProjectConfig config)
+   bool Build(bool isARun, List<ProjectNode> onlyNodes, CompilerConfig compiler, ProjectConfig config, bool justPrint, SingleFileCompileMode mode)
    {
       bool result = false;
       DualPipe f;
       char targetFileName[MAX_LOCATION] = "";
-      char makeTarget[MAX_LOCATION] = "";
+      DynamicString makeTargets { };
       char makeFile[MAX_LOCATION];
       char makeFilePath[MAX_LOCATION];
       char configName[MAX_LOCATION];
       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 command[MAX_F_STRING];
+      char * compilerName;
+
+      compilerName = CopyString(compiler.name);
+      CamelCase(compilerName);
 
       strcpy(configName, config ? config.name : "Common");
-      
+
       SetPath(false, compiler, config); //true
       CatTargetFileName(targetFileName, compiler, config);
 
       strcpy(makeFilePath, topNode.path);
-      CatMakeFileName(makeFile, compiler, config);
+      CatMakeFileName(makeFile, config);
       PathCatSlash(makeFilePath, makeFile);
-      
+
       // TODO: TEST ON UNIX IF \" around makeTarget is ok
-      if(onlyNode)
+      if(onlyNodes)
       {
          if(compiler.type.isVC)
          {
@@ -1465,21 +1769,34 @@ 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\"%s -f \"%s\"",
+                     compiler.makeCommand, cfDir,
+                     crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform,
+                     compilerName, topNode.path, justPrint ? " -n" : "", makeFilePath);
+               if(justPrint)
+                  ide.outputView.buildBox.Logf("%s\n", command);
+               Execute(command);
+            }
+
             ChangeWorkingDir(pushD);
 
-            PathCatSlash(makeTarget+1, objDirExp.dir);
-            PathCatSlash(makeTarget+1, onlyNode.name);
-            StripExtension(makeTarget+1);
-            strcat(makeTarget+1, ".o");
-            makeTarget[0] = '\"';
-            len = strlen(makeTarget);
-            makeTarget[len++] = '\"';
-            makeTarget[len++] = '\0';
+            for(node : onlyNodes)
+            {
+               if(node.GetIsExcluded(config))
+                  ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
+               else
+               {
+                  node.DeleteIntermediateFiles(compiler, config);
+                  node.GetTargets(config, objDirExp.dir, makeTargets);
+               }
+            }
          }
       }
 
@@ -1490,8 +1807,10 @@ private:
          GetWorkingDir(oldwd, sizeof(oldwd));
          ChangeWorkingDir(topNode.path);
 
+         // TODO: support justPrint
          sprintf(command, "%s /useenv /nologo /logcommands %s.sln %s|Win32", compiler.makeCommand, name, config.name);
-         ide.outputView.buildBox.Logf("command: %s\n", command);
+         if(justPrint)
+            ide.outputView.buildBox.Logf("%s\n", command);
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
             ProcessPipeOutputRaw(f);
@@ -1502,37 +1821,81 @@ 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 %sCF_DIR=\"%s\"%s%s COMPILER=%s -j%d %s%s%s -C \"%s\"%s -f \"%s\"",
+               compiler.makeCommand,
+               mode == normal ? "" : (mode == debugPrecompile ? "ECP_DEBUG=y " : mode == debugCompile ? "ECC_DEBUG=y " : mode == debugGenerateSymbols ? "ECS_DEBUG=y " : ""),
+               cfDir,
+               crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform,
+               compilerName, numJobs,
                compiler.ccacheEnabled ? "CCACHE=y " : "",
                compiler.distccEnabled ? "DISTCC=y " : "",
-               makeTarget, topNode.path, makeFilePath);
+               (String)makeTargets, topNode.path, (justPrint || mode != normal) ? " -n" : "", makeFilePath);
+         if(justPrint)
+            ide.outputView.buildBox.Logf("%s\n", command);
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
-            result = ProcessBuildPipeOutput(f, objDirExp, isARun, onlyNode, compiler, config);
+            bool found = false;
+            if(justPrint)
+            {
+               ProcessPipeOutputRaw(f);
+               result = true;
+            }
+            else if(mode != normal)
+            {
+               char line[65536];
+               while(!f.Eof())
+               {
+                  bool result = true;
+                  while(result)
+                  {
+                     if((result = f.Peek()) && (result = f.GetLine(line, sizeof(line)-1)))
+                     {
+                        if(!found && strstr(line, "ide ") == line)
+                        {
+                           strcpy(command, line);
+                           found = true;
+                        }
+                     }
+                  }
+               }
+            }
+            else
+               result = ProcessBuildPipeOutput(f, objDirExp, isARun, onlyNodes, compiler, config);
             delete f;
+            if(found)
+               Execute(command);
          }
          else
             ide.outputView.buildBox.Logf($"Error executing make (%s) command\n", compiler.makeCommand);
       }
 
       delete pathBackup;
-
       delete objDirExp;
+      delete compilerName;
+      delete makeTargets;
       return result;
    }
 
-   void Clean(CompilerConfig compiler, ProjectConfig config, bool realclean)
+   void Clean(CompilerConfig compiler, ProjectConfig config, bool realclean, bool justPrint)
    {
       char makeFile[MAX_LOCATION];
       char makeFilePath[MAX_LOCATION];
       char command[MAX_LOCATION];
+      char * compilerName;
       DualPipe f;
       PathBackup pathBackup { };
+      bool crossCompiling = (compiler.targetPlatform != GetRuntimePlatform());
+      char * targetPlatform = crossCompiling ? (char *)compiler.targetPlatform : "";
+
+      compilerName = CopyString(compiler.name);
+      CamelCase(compilerName);
 
       SetPath(false, compiler, config);
 
       strcpy(makeFilePath, topNode.path);
-      CatMakeFileName(makeFile, compiler, config);
+      CatMakeFileName(makeFile, config);
       PathCatSlash(makeFilePath, makeFile);
       
       if(compiler.type.isVC)
@@ -1542,8 +1905,10 @@ private:
          GetWorkingDir(oldwd, sizeof(oldwd));
          ChangeWorkingDir(topNode.path);
          
+         // TODO: justPrint support
          sprintf(command, "%s /useenv /clean /nologo /logcommands %s.sln %s|Win32", compiler.makeCommand, name, config.name);
-         ide.outputView.buildBox.Logf("command: %s\n", command);
+         if(justPrint)
+            ide.outputView.buildBox.Logf("%s\n", command);
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
             ProcessPipeOutputRaw(f);
@@ -1555,11 +1920,21 @@ private:
       }
       else
       {
-         sprintf(command, "%s %sclean -C \"%s\" -f \"%s\"", compiler.makeCommand, realclean ? "real" : "", topNode.path, makeFilePath);
+         char cfDir[MAX_LOCATION];
+         GetIDECompilerConfigsDir(cfDir, true, true);
+         sprintf(command, "%s CF_DIR=\"%s\"%s%s COMPILER=%s %sclean -C \"%s\"%s -f \"%s\"",
+               compiler.makeCommand, cfDir,
+               crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform,
+               compilerName, realclean ? "real" : "", topNode.path, justPrint ? " -n": "", makeFilePath);
+         if(justPrint)
+            ide.outputView.buildBox.Logf("%s\n", command);
          if((f = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
          {
             ide.outputView.buildBox.Tell($"Deleting target and object files...");
-            ProcessCleanPipeOutput(f, compiler, config);
+            if(justPrint)
+               ProcessPipeOutputRaw(f);
+            else
+               ProcessCleanPipeOutput(f, compiler, config);
             delete f;
 
             ide.outputView.buildBox.Logf($"Target and object files deleted\n");
@@ -1567,6 +1942,7 @@ private:
       }
 
       delete pathBackup;
+      delete compilerName;
    }
 
    void Run(char * args, CompilerConfig compiler, ProjectConfig config)
@@ -1576,7 +1952,7 @@ private:
       DirExpression targetDirExp = GetTargetDir(compiler, config);
       PathBackup pathBackup { };
 
-      // Build(project, ideMain, true, null);
+      // Build(project, ideMain, true, null, false);
 
    #if defined(__WIN32__)
       strcpy(target, topNode.path);
@@ -1619,9 +1995,9 @@ private:
       delete target;
    }
 
-   void Compile(ProjectNode node, CompilerConfig compiler, ProjectConfig config)
+   void Compile(List<ProjectNode> nodes, CompilerConfig compiler, ProjectConfig config, bool justPrint, SingleFileCompileMode mode)
    {
-      Build(false, node, compiler, config);
+      Build(false, nodes, compiler, config, justPrint, mode);
    }
 #endif
 
@@ -1647,8 +2023,197 @@ private:
       }
    }
 
-   bool GenerateMakefile(char * altMakefilePath, bool noResources, char * includemkPath,
-      CompilerConfig compiler, ProjectConfig config)
+   bool GenerateCrossPlatformMk()
+   {
+      bool result = false;
+      char path[MAX_LOCATION];
+
+      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, "crossplatform.mk");
+
+      if(FileExists(path))
+         DeleteFile(path);
+      {
+         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;
+         }
+      }
+      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)
+         {
+            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("# 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);
+               // Moved this to crossplatform.mk
+               //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 := $(if $(ECP_DEBUG),ide -debug-start $(ECERE_SDK_SRC)/compiler/ecp/ecp.epj -@,%s)\n", compiler.ecpCommand);
+            f.Printf("ECC := $(if $(ECC_DEBUG),ide -debug-start $(ECERE_SDK_SRC)/compiler/ecc/ecc.epj -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)\n", compiler.eccCommand);
+            f.Printf("ECS := $(if $(ECS_DEBUG),ide -debug-start $(ECERE_SDK_SRC)/compiler/ecs/ecs.epj -@,%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");
+
+            f.Puts("UPXFLAGS = -9\n"); // TOFEAT: Compression Level Option? Other UPX Options?
+            f.Puts("\n");
+
+            f.Puts("EARFLAGS = \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");
+            f.Puts("\nOFLAGS += $(LDFLAGS)");
+            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];
@@ -1660,18 +2225,10 @@ private:
       if(!altMakefilePath)
       {
          strcpy(filePath, topNode.path);
-         CatMakeFileName(makeFile, compiler, config);
+         CatMakeFileName(makeFile, config);
          PathCatSlash(filePath, makeFile);
       }
 
-#if defined(__WIN32__) && !defined(MAKEFILE_GENERATOR)
-      if(compiler.type.isVC)
-      {
-         GenerateVSSolutionFile(this, compiler);
-         GenerateVCProjectFile(this, compiler);
-      }
-      else
-#endif
       f = FileOpen(altMakefilePath ? altMakefilePath : filePath, write);
 
       /*SetPath(false, compiler, config);
@@ -1690,51 +2247,53 @@ private:
          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 = GetObjDir(compiler, config);
+         String objDirExp = GetObjDirExpression(config);
          TargetTypes targetType = GetTargetType(config);
 
-         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);
-
-         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;
 
-         ReplaceSpaces(objDirNoSpaces, objDirExp.dir);
-         strcpy(targetDir, GetTargetDirExpression(compiler, config));
+         Map<String, int> cflagsVariations { };
+         Map<int, int> nodeCFlagsMapping { };
+
+         Map<String, int> ecflagsVariations { };
+         Map<int, int> nodeECFlagsMapping { };
+
+         ReplaceSpaces(objDirNoSpaces, objDirExp);
+         strcpy(targetDir, GetTargetDirExpression(config));
          ReplaceSpaces(targetDirExpNoSpaces, targetDir);
 
-         strcpy(objDirExpNoSpaces, GetObjDirExpression(compiler, config));
+         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, GetConfigName(config));
-         ReplaceSpaces(fixedCompilerName, compiler.name);
-         //CamelCase(fixedModuleName); // case is important for static linking
          CamelCase(fixedConfigName);
-         CamelCase(fixedCompilerName);
 
          sameObjTargetDirs = !fstrcmp(objDirExpNoSpaces, targetDirExpNoSpaces);
 
-         f.Printf(".PHONY: all objdir%s clean realclean\n\n", sameObjTargetDirs ? "" : " targetdir");
+         f.Printf(".PHONY: all objdir%s cleantarget 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.Puts("ifndef COMPILER\n" "COMPILER := default\n" "endif\n");
+         f.Puts("\n");
+
          test = GetTargetTypeIsSetByPlatform(config);
          if(test)
          {
@@ -1748,278 +2307,203 @@ private:
                if(targetType)
                {
                   if(ifCount)
-                     f.Printf("else\n");
+                     f.Puts("else\n");
                   ifCount++;
-                  f.Printf("ifdef %s\n", PlatformToMakefileVariable(platform));
-
-                  f.Printf("TARGET_TYPE = ");
-                  f.Printf(TargetTypeToMakefileVariable(targetType));
-                  f.Printf("\n");
+                  f.Printf("ifdef %s\n", PlatformToMakefileTargetVariable(platform));
+                  f.Printf("TARGET_TYPE = %s\n", TargetTypeToMakefileVariable(targetType));
                }
             }
-            f.Printf("else\n"); // ifCount should always be > 0
+            f.Puts("else\n");
          }
-         f.Printf("TARGET_TYPE = ");
-         f.Printf(TargetTypeToMakefileVariable(targetType));
-         f.Printf("\n");
+         f.Printf("TARGET_TYPE = %s\n", TargetTypeToMakefileVariable(targetType));
          if(test)
          {
             if(ifCount)
             {
                for(c = 0; c < ifCount; c++)
-                  f.Printf("endif\n");
+                  f.Puts("endif\n");
             }
          }
-         f.Printf("\n");
+         f.Puts("\n");
+
+         f.Puts("# FLAGS\n\n");
 
-         f.Printf("OBJ = %s%s\n\n", objDirExpNoSpaces, objDirExpNoSpaces[0] ? "/" : "");
+         f.Puts("ECFLAGS =\n");
+         f.Puts("ifndef DEBIAN_PACKAGE\n" "CFLAGS =\n" "LDFLAGS =\n" "endif\n");
+         f.Puts("PRJ_CFLAGS =\n");
+         f.Puts("CECFLAGS =\n");
+         f.Puts("OFLAGS =\n");
+         f.Puts("LIBS =\n");
+         f.Puts("\n");
 
-         f.Printf("RES = %s%s\n\n", resDirNoSpaces, resDirNoSpaces[0] ? "/" : "");
+         f.Puts("ifdef DEBUG\n" "NOSTRIP := y\n" "endif\n");
+         f.Puts("\n");
 
-         f.Printf("ifeq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(executable));
+         // 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.Printf("endif\n\n");
+         //f.Puts("endif\n");
+         f.Puts("\n");
+
+         f.Puts("# INCLUDES\n\n");
+
+         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)");
+         }
+
+         f.Printf("_CF_DIR = %s\n", cfDir);
+         f.Puts("\n");
+
+         f.Printf("include %s\n", includemkPath ? includemkPath : "$(_CF_DIR)crossplatform.mk");
+         f.Puts("include $(_CF_DIR)$(TARGET_PLATFORM)-$(COMPILER).cf\n");
+         f.Puts("\n");
+
+         f.Puts("# POST-INCLUDES VARIABLES\n\n");
+
+         f.Printf("OBJ = %s%s\n", objDirExpNoSpaces, objDirExpNoSpaces[0] ? "/" : "");
+         f.Puts("\n");
+
+         f.Printf("RES = %s%s\n", resDirNoSpaces, resDirNoSpaces[0] ? "/" : "");
+         f.Puts("\n");
 
          // test = GetTargetTypeIsSetByPlatform(config);
          {
             char target[MAX_LOCATION];
             char targetNoSpaces[MAX_LOCATION];
-         if(test)
-         {
-            TargetTypes type;
-            ifCount = 0;
-            for(type = (TargetTypes)1; type < TargetTypes::enumSize; type++)
+            if(test)
             {
-               if(type != targetType)
+               TargetTypes type;
+               ifCount = 0;
+               for(type = (TargetTypes)1; type < TargetTypes::enumSize; type++)
                {
-                  if(ifCount)
-                     f.Printf("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);
+                  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);
+                  }
                }
+               f.Puts("else\n");
             }
-            f.Printf("else\n"); // ifCount should always be > 0
-         }
-         GetMakefileTargetFileName(targetType, target, config);
-         strcpy(targetNoSpaces, targetDir);
-         PathCatSlash(targetNoSpaces, target);
-         ReplaceSpaces(targetNoSpaces, targetNoSpaces);
-         f.Printf("TARGET = %s\n", targetNoSpaces);
-         if(test)
-         {
-            if(ifCount)
+            GetMakefileTargetFileName(targetType, target, config);
+            strcpy(targetNoSpaces, targetDir);
+            PathCatSlash(targetNoSpaces, target);
+            ReplaceSpaces(targetNoSpaces, targetNoSpaces);
+            f.Printf("TARGET = %s\n", targetNoSpaces);
+
+            if(test)
             {
-               for(c = 0; c < ifCount; c++)
-                  f.Printf("endif\n");
+               if(ifCount)
+               {
+                  for(c = 0; c < ifCount; c++)
+                     f.Puts("endif\n");
+               }
             }
          }
-         }
-         f.Printf("\n");
+         f.Puts("\n");
 
-         varStringLenDiffs["$(OBJ)"] = strlen(objDirNoSpaces) - 6;
+         // 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);
 
-         numCObjects = topNode.GenMakefilePrintNode(f, this, objects, namesInfo, listItems, config);
-         if(numCObjects)
-            listItems.Add(CopyString("$(OBJ)$(MODULE).main$(O)"));
-         objectsParts = OutputFileList(f, "OBJECTS", listItems, varStringLenDiffs);
-
-         topNode.GenMakefilePrintNode(f, this, cObjects, namesInfo, listItems, config);
-         cobjectsParts = OutputFileList(f, "COBJECTS", listItems, varStringLenDiffs);
-
-         topNode.GenMakefilePrintNode(f, this, symbols, null, listItems, config);
-         symbolsParts = OutputFileList(f, "SYMBOLS", listItems, varStringLenDiffs);
-
-         topNode.GenMakefilePrintNode(f, this, imports, null, listItems, config);
-         importsParts = OutputFileList(f, "IMPORTS", listItems, varStringLenDiffs);
-
-         topNode.GenMakefilePrintNode(f, this, sources, null, listItems, config);
-         OutputFileList(f, "SOURCES", listItems, varStringLenDiffs);
-
-         if(!noResources)
-            resNode.GenMakefilePrintNode(f, this, resources, null, listItems, config);
-         OutputFileList(f, "RESOURCES", listItems, varStringLenDiffs);
-
-         if(includemkPath)
          {
-            f.Printf("# CROSS-PLATFORM MAGIC\n\n");
+            int c;
+            char * map[5][2] = { { "COBJECTS", "C" }, { "SYMBOLS", "S" }, { "IMPORTS", "I" }, { "ECOBJECTS", "O" }, { "BOWLS", "B" } };
 
-            f.Printf("include %s\n\n", includemkPath);
-         }
-         else
-         {
-            File include = FileOpen(":include.mk", read);
-            if(include)
+            numCObjects = topNode.GenMakefilePrintNode(f, this, eCsources, namesInfo, listItems, config, null);
+            if(numCObjects)
             {
-               for(; !include.Eof(); )
+               eCsourcesParts = OutputFileList(f, "_ECSOURCES", listItems, varStringLenDiffs, null);
+
+               f.Puts("ECSOURCES = $(call shwspace,$(_ECSOURCES))\n");
+               if(eCsourcesParts > 1)
                {
-                  char buffer[4096];
-                  int count = include.Read(buffer, 1, 4096);
-                  f.Write(buffer, 1, count);
+                  for(c = 1; c <= eCsourcesParts; c++)
+                     f.Printf("ECSOURCES%d = $(call shwspace,$(_ECSOURCES%d))\n", c, c);
                }
-               delete include;
-            }
-         }
-
-         f.Printf("\n");
+               f.Puts("\n");
 
-         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)
-            {
-               f.Printf("ECS := %s%s%s\n", compiler.ecsCommand,
-                     crossCompiling ? " -t " : "", crossCompiling ? (char*)compiler.targetPlatform : "");
+               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(strcmpi(compiler.earCommand, "ear"))
-               f.Printf("EAR := %s\n", compiler.earCommand);
-            f.Printf("\n");
          }
 
-         f.Printf("# FLAGS\n\n");
+         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");
 
-         f.Printf("CFLAGS =");
-         if(gccCompiler)
-         {
-            f.Printf(" -fmessage-length=0");
-            switch(GetOptimization(config))
-            {
-               case speed:
-                  f.Printf(" -O2");
-                  f.Printf(" -ffast-math");
-                  break;
-               case size:
-                  f.Printf(" -Os");
-                  break;
-            }
-            //if(compiler.targetPlatform.is32Bits)
-               f.Printf(" -m32");
-            //else if(compiler.targetPlatform.is64Bits)
-            //   f.Printf(" -m64");
-            f.Printf(" $(FPIC)");
-            //f.Printf(" -fpack-struct");
-         }
-         switch(GetWarnings(config))
-         {
-            case all: f.Printf(" -Wall"); break;
-            case none: f.Printf(" -w"); break;
-         }
-         if(GetDebug(config))
-            f.Printf(" -g");
-         if(GetProfile(config))
-            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);
-         if(compiler.includeDirs)
-            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(GetMemoryGuard(config))
-            f.Printf(" -memguard");
-         if(GetStrictNameSpaces(config))
-            f.Printf(" -strictns");
-         if(GetNoLineNumbers(config))
-            f.Printf(" -nolinenumbers");
-         {
-            char * s;
-            if((s = GetDefaultNameSpace(config)) && s[0])
-               f.Printf(" -defaultns %s", s);
-         }
-         f.Printf("\n\n");
-
-         f.Printf("OFLAGS =\n");
-         f.Printf("ifneq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(staticLibrary));
-         f.Printf("OFLAGS += -m32");
-         if(GetProfile(config))
-            f.Printf(" -pg");
-         // no if?
-         if(compiler.libraryDirs)
-            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");
-         f.Printf("endif\n\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.Printf("LIBS =\n");
-            f.Printf("ifneq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(staticLibrary));
-            f.Printf("LIBS +=");
+            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.Printf("\n");
-            f.Printf("endif\n");
-            f.Printf("LIBS +=");
+            f.Puts("\n");
+            f.Puts("endif\n");
+            f.Puts("\n");
          }
-         else
-            f.Printf("LIBS =");
 
-         f.Printf(" $(SHAREDLIB) $(EXECUTABLE) $(LINKOPT)\n\n");
+         topNode.GenMakeCollectAssignNodeFlags(config, numCObjects,
+               cflagsVariations, nodeCFlagsMapping,
+               ecflagsVariations, nodeECFlagsMapping, null);
 
-         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");
-
-         // JF's
-         f.Printf("ifdef %s\n", PlatformToMakefileVariable(apple));
-         f.Printf("OFLAGS += -framework cocoa -framework OpenGL\n");
-         f.Printf("endif\n\n");
+         GenMakePrintCustomFlags(f, "PRJ_CFLAGS", false, cflagsVariations);
+         GenMakePrintCustomFlags(f, "ECFLAGS", true, ecflagsVariations);
 
          if(platforms || (config && config.platforms))
          {
             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, configPlatformOptions;
@@ -2028,39 +2512,55 @@ private:
                if(projectPlatformOptions || configPlatformOptions)
                {
                   if(ifCount)
-                     f.Printf("else\n");
+                     f.Puts("else\n");
                   ifCount++;
-                  f.Printf("ifdef ");
-                  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("OFLAGS +=");
                      if(projectPlatformOptions && projectPlatformOptions.options.linkerOptions && projectPlatformOptions.options.linkerOptions.count)
                      {
-                        f.Printf(" \\\n\t -Wl");
+                        bool needWl = false;
+                        f.Puts(" \\\n\t ");
                         for(s : projectPlatformOptions.options.linkerOptions)
-                           f.Printf(",%s", s);
+                        {
+                           if(!IsLinkerOption(s))
+                              f.Printf(" %s", s);
+                           else
+                              needWl = true;
+                        }
+                        if(needWl)
+                        {
+                           f.Puts(" -Wl");
+                           for(s : projectPlatformOptions.options.linkerOptions)
+                              if(IsLinkerOption(s))
+                                 f.Printf(",%s", s);
+                        }
                      }
                      if(configPlatformOptions && configPlatformOptions.options.linkerOptions && configPlatformOptions.options.linkerOptions.count)
                      {
-                        f.Printf(" \\\n\t -Wl");
+                        bool needWl = false;
+                        f.Puts(" \\\n\t ");
                         for(s : configPlatformOptions.options.linkerOptions)
-                           f.Printf(",%s", s);
+                        {
+                           if(IsLinkerOption(s))
+                              f.Printf(" %s", s);
+                           else
+                              needWl = true;
+                        }
+                        if(needWl)
+                        {
+                           f.Puts(" -Wl");
+                           for(s : configPlatformOptions.options.linkerOptions)
+                              if(!IsLinkerOption(s))
+                                 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((projectPlatformOptions && projectPlatformOptions.options.libraryDirs && projectPlatformOptions.options.libraryDirs.count) ||
@@ -2068,51 +2568,133 @@ private:
                         (projectPlatformOptions && projectPlatformOptions.options.libraries && projectPlatformOptions.options.libraries.count) ||
                         (configPlatformOptions && configPlatformOptions.options.libraries && configPlatformOptions.options.libraries.count))
                   {
-                     f.Printf("ifneq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(staticLibrary));
+                     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");
+                        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");
+                        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");
-                     }}
-                     f.Printf("endif\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");
+                  f.Puts("endif\n");
             }
-            f.Printf("\n");
+            f.Puts("\n");
          }
 
-         f.Printf("# TARGETS\n\n");
+         if((config && config.options && config.options.linkerOptions && config.options.linkerOptions.count) ||
+               (options && options.linkerOptions && options.linkerOptions.count))
+         {
+            f.Puts("OFLAGS +=");
+            f.Puts(" \\\n\t");
 
-         f.Printf("all: objdir%s $(TARGET)\n\n", sameObjTargetDirs ? "" : " targetdir");
+            if(config && config.options && config.options.linkerOptions && config.options.linkerOptions.count)
+            {
+               bool needWl = false;
+               for(s : config.options.linkerOptions)
+               {
+                  if(!IsLinkerOption(s))
+                     f.Printf(" %s", s);
+                  else
+                     needWl = true;
+               }
+               if(needWl)
+               {
+                  f.Puts(" -Wl");
+                  for(s : config.options.linkerOptions)
+                     if(IsLinkerOption(s))
+                        f.Printf(",%s", s);
+               }
+            }
+            if(options && options.linkerOptions && options.linkerOptions.count)
+            {
+               bool needWl = false;
+               for(s : options.linkerOptions)
+               {
+                  if(!IsLinkerOption(s))
+                     f.Printf(" %s", s);
+                  else
+                     needWl = true;
+               }
+               if(needWl)
+               {
+                  f.Puts(" -Wl");
+                  for(s : options.linkerOptions)
+                     if(IsLinkerOption(s))
+                        f.Printf(",%s", s);
+               }
+            }
+         }
+         f.Puts("\n");
+         f.Puts("\n");
+
+         f.Puts("CECFLAGS += -cpp $(_CPP)");
+         f.Puts("\n");
+         f.Puts("\n");
+
+         forceBitDepth = (options && options.buildBitDepth) || numCObjects;
+         if(forceBitDepth || GetProfile(config))
+         {
+            f.Puts("OFLAGS +=");
+            if(forceBitDepth)
+               f.Puts((!options || !options.buildBitDepth || options.buildBitDepth == bits32) ? " $(FORCE_32_BIT)" : " $(FORCE_64_BIT)");
+            if(GetProfile(config))
+               f.Puts(" -pg");
+            f.Puts("\n");
+            f.Puts("\n");
+         }
+
+         if((config && config.options && config.options.libraryDirs) || (options && options.libraryDirs))
+         {
+            f.Puts("ifndef STATIC_LIBRARY_TARGET\n");
+            f.Puts("OFLAGS +=");
+            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.Printf("objdir:\n");
-            f.Printf("\t$(if $(wildcard $(OBJ)),,$(call mkdirq,$(OBJ)))\n");
-         //f.Printf("# PRE-BUILD COMMANDS\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)
@@ -2126,7 +2708,7 @@ private:
          if(platforms || (config && config.platforms))
          {
             ifCount = 0;
-            //f.Printf("# PLATFORM-SPECIFIC PRE-BUILD COMMANDS\n");
+            //f.Puts("# TARGET_PLATFORM-SPECIFIC PRE-BUILD COMMANDS\n");
             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
             {
                PlatformOptions projectPOs, configPOs;
@@ -2136,11 +2718,9 @@ private:
                      (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)
                   {
@@ -2158,30 +2738,33 @@ private:
             {
                int c;
                for(c = 0; c < ifCount; c++)
-                  f.Printf("endif\n");
+                  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", 
+            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) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_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) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY)"
+                  " -c $(OBJ)$(MODULE).main.ec -o $(OBJ)$(MODULE).main.c -symbols $(OBJ)\n");
+            f.Puts("\n");
          }
 
          // *** Target ***
@@ -2190,38 +2773,38 @@ private:
          // f.Printf("$(TARGET): $(SOURCES) $(RESOURCES) | objdir $(SYMBOLS) $(OBJECTS)%s\n", sameObjTargetDirs ? "" : " targetdir");
 
          // This should fix it for good!
-         f.Printf("$(SYMBOLS): | objdir\n");
-         f.Printf("$(OBJECTS): | objdir\n");
+         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");
 
-         f.Printf("ifneq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(staticLibrary));
-         f.Printf("\t$(CC) $(OFLAGS) $(OBJECTS) $(LIBS) -o $(TARGET) $(INSTALLNAME)\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.Printf("ifndef NOSTRIP\n");
-            f.Printf("\t$(STRIP) $(STRIPOPT) $(TARGET)\n");
-            f.Printf("endif\n");
+            f.Puts("ifndef NOSTRIP\n");
+            f.Puts("\t$(STRIP) $(STRIPOPT) $(TARGET)\n");
+            f.Puts("endif\n");
 
             if(GetCompress(config))
             {
-               f.Printf("ifndef WINDOWS\n");
-               f.Printf("ifeq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(executable));
-                  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.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, config);
-         f.Printf("else\n");
-         f.Printf("\t$(AR) rcs $(TARGET) $(OBJECTS) $(LIBS)\n");
-         f.Printf("endif\n");
+         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)
@@ -2235,7 +2818,7 @@ private:
          if(platforms || (config && config.platforms))
          {
             ifCount = 0;
-            //f.Printf("# PLATFORM-SPECIFIC POST-BUILD COMMANDS\n");
+            //f.Puts("# TARGET_PLATFORM-SPECIFIC POST-BUILD COMMANDS\n");
             for(platform = (Platform)1; platform < Platform::enumSize; platform++)
             {
                PlatformOptions projectPOs, configPOs;
@@ -2245,11 +2828,9 @@ private:
                      (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)
                   {
@@ -2267,51 +2848,60 @@ private:
             {
                int c;
                for(c = 0; c < ifCount; c++)
-                  f.Printf("endif\n");
+                  f.Puts("endif\n");
             }
          }
-         f.Printf("\n");
+         f.Puts("\n");
 
-         f.Printf("# SYMBOL RULES\n\n");
-         topNode.GenMakefilePrintSymbolRules(f, this, compiler, config);
+         f.Puts("# SYMBOL RULES\n");
+         f.Puts("\n");
 
-         f.Printf("# C OBJECT RULES\n\n");
-         topNode.GenMakefilePrintCObjectRules(f, this, compiler, config);
+         topNode.GenMakefilePrintSymbolRules(f, this, config, nodeCFlagsMapping, nodeECFlagsMapping);
 
-         /*if(numCObjects)
-         {
-            f.Printf("# IMPLICIT OBJECT RULE\n\n");
+         f.Puts("# C OBJECT RULES\n");
+         f.Puts("\n");
 
-            f.Printf("$(OBJ)%%$(O) : $(OBJ)%%.c\n");
-            f.Printf("\t$(CC) $(CFLAGS) $(FVISIBILITY) -c $< -o $@\n\n");
-         }*/
+         topNode.GenMakefilePrintCObjectRules(f, this, config, nodeCFlagsMapping, nodeECFlagsMapping);
 
-         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, compiler, config);
+         topNode.GenMakefilePrintObjectRules(f, this, namesInfo, config, nodeCFlagsMapping, nodeECFlagsMapping);
 
          if(numCObjects)
-            GenMakefilePrintMainObjectRule(f, compiler, config);
+            GenMakefilePrintMainObjectRule(f, config);
+
+         f.Printf("cleantarget: objdir%s\n", sameObjTargetDirs ? "" : " targetdir");
+         f.Puts("\t$(call rmq,$(TARGET))\n");
+         f.Puts("\n");
 
-         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.Puts("clean: cleantarget\n");
+         OutputCleanActions(f, "_OBJECTS", objectsParts);
          if(numCObjects)
          {
-            OutputCleanActions(f, "IMPORTS", importsParts);
-            OutputCleanActions(f, "SYMBOLS", symbolsParts);
+            f.Printf("\t$(call rmq,%s)\n", "$(OBJ)$(MODULE).main.o $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)");
+            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("realclean: clean\n");
-         f.Printf("\t$(call rmrq,$(OBJ))\n");
+         f.Puts("realclean: cleantarget\n");
+         f.Puts("\t$(call rmrq,$(OBJ))\n");
          if(!sameObjTargetDirs)
             f.Printf("\t$(call rmdirq,%s)\n", targetDirExpNoSpaces);
+         f.Puts("\n");
+
+         f.Puts("distclean: cleantarget\n");
+         if(!sameObjTargetDirs)
+            f.Printf("\t$(call rmdirq,%s)\n", targetDirExpNoSpaces);
+         f.Puts("\t$(call rmrq,obj/)\n");
 
          delete f;
-         delete objDirExp;
+
          listItems.Free();
          delete listItems;
          varStringLenDiffs.Free();
@@ -2319,6 +2909,11 @@ private:
          namesInfo.Free();
          delete namesInfo;
 
+         delete cflagsVariations;
+         delete nodeCFlagsMapping;
+         delete ecflagsVariations;
+         delete nodeECFlagsMapping;
+
          result = true;
       }
 
@@ -2330,7 +2925,7 @@ private:
       return result;
    }
 
-   void GenMakefilePrintMainObjectRule(File f, CompilerConfig compiler, ProjectConfig config)
+   void GenMakefilePrintMainObjectRule(File f, ProjectConfig config)
    {
       char extension[MAX_EXTENSION] = "c";
       char modulePath[MAX_LOCATION];
@@ -2338,9 +2933,9 @@ private:
       DualPipe dep;
       char command[2048];
       char objDirNoSpaces[MAX_LOCATION];
-      DirExpression objDirExp = GetObjDir(compiler, config);
+      String objDirExp = GetObjDirExpression(config);
 
-      ReplaceSpaces(objDirNoSpaces, objDirExp.dir);
+      ReplaceSpaces(objDirNoSpaces, objDirExp);
       ReplaceSpaces(fixedModuleName, moduleName);
       
       //sprintf(fixedModuleName, "%s.main", fixedPrjName);
@@ -2415,15 +3010,36 @@ 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");
+            f.Printf("\t$(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.%s -o $(OBJ)$(MODULE).main$(O)\n", extension);
+            f.Puts("\n");
 #if 0
          }
       }
 #endif
+   }
 
-      f.Printf("\t$(CC) $(CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.%s -o $(OBJ)$(MODULE).main$(O)\n\n", extension);
-
-      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,
@@ -2788,13 +3404,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")))
             {
@@ -3267,7 +3887,7 @@ void CombineIdenticalConfigOptions(Project project)
    }
 }
 
-Project LoadProject(char * filePath)
+Project LoadProject(char * filePath, char * activeConfigName)
 {
    Project project = null;
    File f = FileOpen(filePath, read);
@@ -3316,6 +3936,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;