ide: fix specifying all file extensions (output file extension for emscripten) inside...
[sdk] / ide / src / project / Project.ec
index 5e2c553..9953d65 100644 (file)
@@ -6,7 +6,7 @@ public import "ecere"
 
 import "DynamicString"
 
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
 import "ide"
 // We should have the .sln/.vcproj generation even on other platforms
 // e.g. detect from an environment variable pointing to a Windows drive
@@ -32,26 +32,7 @@ private:
 extern int __ecereVMethodID_class_OnCompare;
 extern int __ecereVMethodID_class_OnFree;
 
-IDESettings ideSettings;
-
-IDESettingsContainer settingsContainer
-{
-   driver = "JSON";
-   dataOwner = &ideSettings;
-   dataClass = class(IDESettings);
-
-   void OnLoad(GlobalSettingsData data)
-   {
-#ifndef MAKEFILE_GENERATOR
-      IDESettings settings = (IDESettings)data;
-      globalSettingsDialog.ideSettings = settings;
-      ide.UpdateRecentMenus();
-      ide.UpdateCompilerConfigs(true);
-#endif
-   }
-};
-
-#ifdef MAKEFILE_GENERATOR
+#if defined(ECERE_DOCUMENTOR) || defined(ECERE_EPJ2MAKE)
 CompilerConfig defaultCompiler;
 #endif
 
@@ -406,7 +387,7 @@ static bool IsLinkerOption(String s)
 
 static byte epjSignature[] = { 'E', 'P', 'J', 0x04, 0x01, 0x12, 0x03, 0x12 };
 
-enum GenMakefilePrintTypes { objects, cObjects, symbols, imports, sources, resources, eCsources, rcSources };
+enum GenMakefilePrintTypes { noPrint, objects, cObjects, symbols, imports, sources, resources, eCsources, rcSources };
 
 define WorkspaceExtension = "ews";
 define ProjectExtension = "epj";
@@ -587,9 +568,11 @@ void OutputFileListActions(File f, const char * name, int parts, const char * fi
    {
       int c;
       for(c=0; c<parts; c++)
-         f.Printf("\t@$(call echo,$(%s%d)) >> %s\n", name, c+1, fileName);
-   } else if(parts) {
-      f.Printf("\t@$(call echo,$(%s)) >> %s\n", name, fileName);
+         f.Printf("\t$(call addtolistfile,$(%s%d),%s)\n", name, c+1, fileName);
+   }
+   else if(parts)
+   {
+      f.Printf("\t$(call addtolistfile,$(%s),%s)\n", name, fileName);
    }
 }
 
@@ -599,10 +582,10 @@ void OutputCleanActions(File f, const char * name, int parts)
    {
       int c;
       for(c=0; c<parts; c++)
-         f.Printf("\t$(call rmq,$(_%s%d))\n", name, c+1);
+         f.Printf("\t$(call rm,$(_%s%d))\n", name, c+1);
    }
    else
-      f.Printf("\t$(call rmq,$(_%s))\n", name);
+      f.Printf("\t$(call rm,$(_%s))\n", name);
 }
 
 enum LineOutputMethod { inPlace, newLine, lineEach };
@@ -677,10 +660,10 @@ void CamelCase(char * string)
 
 CompilerConfig GetCompilerConfig()
 {
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
    CompilerConfig compiler = null;
    if(ide && ide.workspace)
-      compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      compiler = ideSettings.GetCompilerConfig(ide.workspace.activeCompiler);
    return compiler;
 #else
    incref defaultCompiler;
@@ -688,6 +671,15 @@ CompilerConfig GetCompilerConfig()
 #endif
 }
 
+int GetBitDepth()
+{
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+   return ide.workspace ? ide.workspace.bitDepth : 0;
+#else
+   return 0; // todo: improve this somehow? add bit depth command line option?
+#endif
+}
+
 define localTargetType = config && config.options && config.options.targetType ?
             config.options.targetType : options && options.targetType ?
             options.targetType : TargetTypes::executable;
@@ -872,7 +864,7 @@ private:
 
    Map<String, Map<String, NameCollisionInfo>> configsNameCollisions { };
 
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
    FileMonitor fileMonitor
    {
       this, FileChange { modified = true };
@@ -934,7 +926,7 @@ private:
 
                if(projectView)
                {
-                  CompilerConfig compiler = ideSettings.GetCompilerConfig(projectView.workspace.compiler);
+                  CompilerConfig compiler = ideSettings.GetCompilerConfig(projectView.workspace.activeCompiler);
                   projectView.AddNode(topNode, null);
                   topNode.row.Move(prev);
 
@@ -950,7 +942,7 @@ private:
          }
          return true;
       }
-      return true;
+      return !ide.destroyed;
    }
 
 #endif
@@ -1038,7 +1030,7 @@ private:
       return result;
    }
 
-   ProjectNode FindNodeByObjectFileName(const char * fileName, IntermediateFileType type, bool dotMain, ProjectConfig config)
+   ProjectNode FindNodeByObjectFileName(const char * fileName, IntermediateFileType type, bool dotMain, ProjectConfig config, const char * objectFileExt)
    {
       ProjectNode result;
       const char * cfgName;
@@ -1047,7 +1039,7 @@ private:
       cfgName = config ? config.name : "";
       if(!configsNameCollisions[cfgName])
          ProjectLoadLastBuildNamesInfo(this, config);
-      result = topNode.FindByObjectFileName(fileName, type, dotMain, configsNameCollisions[cfgName]);
+      result = topNode.FindByObjectFileName(fileName, type, dotMain, configsNameCollisions[cfgName], objectFileExt);
       return result;
    }
 
@@ -1181,7 +1173,7 @@ private:
 
    bool GetConfigIsInActiveDebugSession(ProjectConfig config)
    {
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
       return ide.project == this && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isActive;
 #else
       return false;
@@ -1190,7 +1182,7 @@ private:
 
    bool GetConfigIsInDebugSession(ProjectConfig config)
    {
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
       return ide.project == this && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isPrepared;
 #else
       return false;
@@ -1199,18 +1191,15 @@ private:
 
    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
    {
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
       ide.SetPath(projectsDirs, compiler, config, bitDepth);
 #endif
    }
 
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
    bool Save(const char * fileName)
    {
       File f;
-      /*char output[MAX_LOCATION];
-       ChangeExtension(fileName, "json", output);
-      f = FileOpen(output, write);*/
       f = FileOpen(fileName, write);
       if(f)
       {
@@ -1254,11 +1243,21 @@ private:
       switch(targetType)
       {
          case executable:
-            if(compiler.targetPlatform == win32)
+            if(compiler.executableFileExt)
+            {
+               strcat(string, ".");
+               strcat(string, compiler.executableFileExt);
+            }
+            else if(compiler.targetPlatform == win32)
                strcat(string, ".exe");
             break;
          case sharedLibrary:
-            if(compiler.targetPlatform == win32)
+            if(compiler.sharedLibFileExt)
+            {
+               strcat(string, ".");
+               strcat(string, compiler.sharedLibFileExt);
+            }
+            else if(compiler.targetPlatform == win32)
                strcat(string, ".dll");
             else if(compiler.targetPlatform == apple)
                strcat(string, ".dylib");
@@ -1271,7 +1270,13 @@ private:
             }
             break;
          case staticLibrary:
-            strcat(string, ".a");
+            if(compiler.staticLibFileExt)
+            {
+               strcat(string, ".");
+               strcat(string, compiler.staticLibFileExt);
+            }
+            else
+               strcat(string, ".a");
             break;
       }
    }
@@ -1348,8 +1353,8 @@ private:
       sprintf(string, "%s%s%s.Makefile", projectName, config ? "-" : "", config ? config.name : "");
    }
 
-#ifndef MAKEFILE_GENERATOR
-   ProjectNode GetObjectFileNode(const char * filePath)
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+   ProjectNode GetObjectFileNode(const char * filePath, const char * objectFileExt)
    {
       ProjectNode node = null;
       char ext[MAX_EXTENSION];
@@ -1364,18 +1369,18 @@ private:
             if(fileName[0])
             {
                DotMain dotMain = DotMain::FromFileName(fileName);
-               node = FindNodeByObjectFileName(fileName, type, dotMain, null);
+               node = FindNodeByObjectFileName(fileName, type, dotMain, null, objectFileExt);
             }
          }
       }
       return node;
    }
 
-   bool GetAbsoluteFromRelativePath(const char * relativePath, char * absolutePath)
+   bool GetAbsoluteFromRelativePath(const char * relativePath, char * absolutePath, const char * objectFileExt)
    {
       ProjectNode node = topNode.FindWithPath(relativePath, false);
       if(!node)
-         node = GetObjectFileNode(relativePath);
+         node = GetObjectFileNode(relativePath, objectFileExt);
       if(node)
       {
          strcpy(absolutePath, node.project.topNode.path);
@@ -1557,8 +1562,8 @@ private:
       cc.concatx((String)prefix, compiler.ccCommand,  " ");
       cxx.concatx((String)prefix, compiler.cxxCommand, " ");
 
+      ar.concatx((String)gnuToolchainPrefix, compiler.arCommand,  " ");
       strip.concatx(gnuToolchainPrefix, "strip ");
-      ar.concatx(gnuToolchainPrefix, "ar rcs");
       windres.concatx(gnuToolchainPrefix, "windres ");
 
       testLen = Max(testLen, ecp.size);
@@ -2081,7 +2086,7 @@ private:
       }
    }
 
-   bool Build(BuildType buildType, List<ProjectNode> onlyNodes, CompilerConfig compiler, ProjectConfig config, int bitDepth, bool justPrint, SingleFileCompileMode mode)
+   bool Build(BuildType buildType, List<ProjectNode> onlyNodes, CompilerConfig compiler, ProjectConfig config, int bitDepth, BuildOutputMode outputMode, SingleFileCompileMode mode)
    {
       bool result = false;
       DualPipe f;
@@ -2139,15 +2144,15 @@ private:
             // Create object dir if it does not exist already
             if(!FileExists(objDirExp.dir).isDirectory)
             {
-               sprintf(command, "%s CF_DIR=\"%s\"%s%s%s%s%s COMPILER=%s objdir -C \"%s\"%s -f \"%s\"",
+               sprintf(command, "%s CF_DIR=\"%s\"%s%s%s%s%s COMPILER=%s objdir -C \"%s\"%s%s -f \"%s\"",
                      compiler.makeCommand, cfDir,
                      crossCompiling ? " TARGET_PLATFORM=" : "",
                      targetPlatform,
                      bitDepth ? " ARCH=" : "", bitDepth == 32 ? "32" : bitDepth == 64 ? "64" : "",
                      /*(bitDepth == 64 && compiler.targetPlatform == win32) ? " GCC_PREFIX=x86_64-w64-mingw32-" : (bitDepth == 32 && compiler.targetPlatform == win32) ? " GCC_PREFIX=i686-w64-mingw32-" : */"",
-
-                     compilerName, topNode.path, justPrint ? " -n" : "", makeFilePath);
-               if(justPrint)
+                     compilerName,
+                     topNode.path, outputMode == verbose ? " V=1" : "", outputMode == justPrint ? " -n" : "", makeFilePath);
+               if(outputMode != normal)
                   ide.outputView.buildBox.Logf("%s\n", command);
                Execute(command);
             }
@@ -2162,7 +2167,7 @@ private:
                {
                   if(!eC_Debug)
                      node.DeleteIntermediateFiles(compiler, config, bitDepth, cfgNameCollisions, mode == cObject ? true : false);
-                  node.GetTargets(config, cfgNameCollisions, objDirExp.dir, makeTargets);
+                  node.GetTargets(config, cfgNameCollisions, objDirExp.dir, compiler.objectFileExt, makeTargets);
                }
             }
          }
@@ -2175,9 +2180,9 @@ private:
          GetWorkingDir(oldwd, sizeof(oldwd));
          ChangeWorkingDir(topNode.path);
 
-         // TODO: support justPrint
+         // TODO: support justPrint and verbose
          sprintf(command, "%s /useenv /nologo /logcommands %s.sln %s|Win32", compiler.makeCommand, name, config.name);
-         if(justPrint)
+         if(outputMode != normal)
             ide.outputView.buildBox.Logf("%s\n", command);
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true/*, input = true*/ }, command)))
          {
@@ -2197,7 +2202,7 @@ private:
          GccVersionInfo cxxVersion = GetGccVersionInfo(compiler, compiler.cxxCommand);
          char cfDir[MAX_LOCATION];
          GetIDECompilerConfigsDir(cfDir, true, true);
-         sprintf(command, "%s%s %sCF_DIR=\"%s\"%s%s%s%s%s%s COMPILER=%s %s%s%s-j%d %s%s%s -C \"%s\"%s -f \"%s\"",
+         sprintf(command, "%s%s %sCF_DIR=\"%s\"%s%s%s%s%s%s COMPILER=%s%s %s%s%s-j%d %s%s%s -C \"%s\"%s%s -f \"%s\"",
 #if defined(__WIN32__)
                "",
 #else
@@ -2212,14 +2217,16 @@ private:
                bitDepth == 32 ? "32" : bitDepth == 64 ? "64" : "",
                ide.workspace.useValgrind ? " DISABLED_POOLING=1" : "",
                /*(bitDepth == 64 && compiler.targetPlatform == win32) ? " GCC_PREFIX=x86_64-w64-mingw32-" : (bitDepth == 32 && compiler.targetPlatform == win32) ? " GCC_PREFIX=i686-w64-mingw32-" :*/ "",
-               compilerName, eC_Debug ? "--always-make " : "",
+               compilerName,
+               compiler.noStripTarget ? " NOSTRIP=y" : "",
+               eC_Debug ? "--always-make " : "",
                ccVersion == post4_8 ? "GCC_CC_FLAGS=-fno-diagnostics-show-caret " : "",
                cxxVersion == post4_8 ? "GCC_CXX_FLAGS=-fno-diagnostics-show-caret " : "",
                numJobs,
                (compiler.ccacheEnabled && !eC_Debug) ? "CCACHE=y " : "",
                (compiler.distccEnabled && !eC_Debug) ? "DISTCC=y " : "",
-               (String)makeTargets, topNode.path, (justPrint || eC_Debug) ? " -n" : "", makeFilePath);
-         if(justPrint)
+               (String)makeTargets, topNode.path, outputMode == verbose ? " V=1" : "", (outputMode == justPrint || eC_Debug) ? " -n" : "", makeFilePath);
+         if(outputMode != normal)
             ide.outputView.buildBox.Logf("%s\n", command);
 
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
@@ -2229,7 +2236,7 @@ private:
             if(eC_Debug)
             {
                char line[65536];
-               if(justPrint)
+               if(outputMode == justPrint)
                   ide.outputView.buildBox.Logf($"\nMake outputs the following list of commands to choose from:\n");
                while(!f.Eof())
                {
@@ -2238,14 +2245,14 @@ private:
                   {
                      if((result = f.Peek()) && (result = f.GetLine(line, sizeof(line)-1)) && line[0])
                      {
-                        if(justPrint)
+                        if(outputMode == justPrint)
                            ide.outputView.buildBox.Logf("%s\n", line);
                         if(!error && !found && strstr(line, "echo ") == line && strstr(line, "ECERE_SDK_SRC"))
                         {
                            strcpy(command, line+5);
                            error = true;
                         }
-                        if(!error && (singleProjectOnlyNode || !found) && strstr(line, "ide ") == line)
+                        if(!error && (singleProjectOnlyNode || !found) && strstr(line, "ecere-ide ") == line)
                         {
                            strcpy(command, line);
                            found = true;
@@ -2256,14 +2263,14 @@ private:
                if(found)
                   result = true;
             }
-            else if(justPrint)
+            else if(outputMode != normal)
                result = ProcessPipeOutputRaw(f);
             else
                result = ProcessBuildPipeOutput(f, objDirExp, buildType, onlyNodes, compiler, config, bitDepth);
             delete f;
             if(error)
                ide.outputView.buildBox.Logf("%s\n", command);
-            else if(justPrint && found)
+            else if(outputMode == justPrint && found)
                ide.outputView.buildBox.Logf($"\nThe following command was chosen to be executed:\n%s\n", command);
             else if(found)
                Execute(command);
@@ -2279,7 +2286,7 @@ private:
       return result;
    }
 
-   void Clean(CompilerConfig compiler, ProjectConfig config, int bitDepth, CleanType cleanType, bool justPrint)
+   void Clean(CompilerConfig compiler, ProjectConfig config, int bitDepth, CleanType cleanType, BuildOutputMode outputMode)
    {
       char makeFile[MAX_LOCATION];
       char makeFilePath[MAX_LOCATION];
@@ -2306,9 +2313,9 @@ private:
          GetWorkingDir(oldwd, sizeof(oldwd));
          ChangeWorkingDir(topNode.path);
 
-         // TODO: justPrint support
+         // TODO: support justPrint and verbose
          sprintf(command, "%s /useenv /clean /nologo /logcommands %s.sln %s|Win32", compiler.makeCommand, name, config.name);
-         if(justPrint)
+         if(outputMode != normal)
             ide.outputView.buildBox.Logf("%s\n", command);
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
@@ -2323,21 +2330,21 @@ private:
       {
          char cfDir[MAX_LOCATION];
          GetIDECompilerConfigsDir(cfDir, true, true);
-         sprintf(command, "%s CF_DIR=\"%s\"%s%s%s%s COMPILER=%s %sclean%s -C \"%s\"%s -f \"%s\"",
+         sprintf(command, "%s CF_DIR=\"%s\"%s%s%s%s COMPILER=%s %sclean%s -C \"%s\"%s%s -f \"%s\"",
                compiler.makeCommand, cfDir,
                crossCompiling ? " TARGET_PLATFORM=" : "", targetPlatform,
                bitDepth ? " ARCH=" : "", bitDepth == 32 ? "32" : bitDepth == 64 ? "64" : "",
                compilerName,
                cleanType == realClean ? "real" : "", cleanType == cleanTarget ? "target" : "",
-               topNode.path, justPrint ? " -n": "", makeFilePath);
-         if(justPrint)
+               topNode.path, outputMode == verbose ? " V=1" : "", outputMode == justPrint ? " -n": "", makeFilePath);
+         if(outputMode != normal)
             ide.outputView.buildBox.Logf("%s\n", command);
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
             ide.outputView.buildBox.Tellf($"Deleting %s%s...",
                   cleanType == realClean ? $"intermediate objects directory" : $"target",
                   cleanType == clean ? $" and object files" : "");
-            if(justPrint)
+            if(outputMode != normal)
                ProcessPipeOutputRaw(f);
             else
                ProcessCleanPipeOutput(f, compiler, config);
@@ -2352,7 +2359,7 @@ private:
       delete compilerName;
    }
 
-   void Run(const char * args, CompilerConfig compiler, ProjectConfig config, int bitDepth)
+   void Run(const char * args, CompilerConfig compiler, ProjectConfig config, int bitDepth, bool shellOpen)
    {
       String target = new char[maxPathLen];
       char oldDirectory[MAX_LOCATION];
@@ -2360,15 +2367,16 @@ private:
       DirExpression targetDirExp = GetTargetDir(compiler, config, bitDepth);
       PathBackup pathBackup { };
 
-      // Build(project, ideMain, true, null, false);
+      // Build(project, ...);
 
       strcpy(target, topNode.path);
       PathCatSlash(target, targetDirExp.dir);
       CatTargetFileName(target, compiler, config);
-      sprintf(target, "%s %s", target, args);
+      if(args[0] && (executableLauncher || !shellOpen))
+         sprintf(target, "%s %s", target, args);
       GetWorkingDir(oldDirectory, MAX_LOCATION);
 
-      if(strlen(ide.workspace.debugDir))
+      if(ide.workspace.debugDir && strlen(ide.workspace.debugDir))
       {
          char temp[MAX_LOCATION];
          strcpy(temp, topNode.path);
@@ -2388,6 +2396,8 @@ private:
          Execute(prefixedTarget);
          delete prefixedTarget;
       }
+      else if(shellOpen)
+         ShellOpen(target);
       else
          Execute(target);
 
@@ -2398,9 +2408,9 @@ private:
       delete target;
    }
 
-   bool Compile(List<ProjectNode> nodes, CompilerConfig compiler, ProjectConfig config, int bitDepth, bool justPrint, SingleFileCompileMode mode)
+   bool Compile(List<ProjectNode> nodes, CompilerConfig compiler, ProjectConfig config, int bitDepth, BuildOutputMode outputMode, SingleFileCompileMode mode)
    {
-      return Build(build, nodes, compiler, config, bitDepth, justPrint, mode);
+      return Build(build, nodes, compiler, config, bitDepth, outputMode, mode);
    }
 #endif
 
@@ -2410,18 +2420,7 @@ private:
       if(targetType == staticLibrary || targetType == sharedLibrary)
          strcat(fileName, "$(LP)");
       strcat(fileName, GetTargetFileName(config));
-      switch(targetType)
-      {
-         case executable:
-            strcat(fileName, "$(E)");
-            break;
-         case sharedLibrary:
-            strcat(fileName, "$(SO)$(VER)");
-            break;
-         case staticLibrary:
-            strcat(fileName, "$(A)");
-            break;
-      }
+      strcat(fileName, "$(OUT)");
    }
 
    bool GenerateCrossPlatformMk(File altCrossPlatformMk)
@@ -2512,14 +2511,56 @@ private:
                f.Puts("\n");
                for(e : compiler.environmentVars)
                {
+#if defined(__WIN32__)
+                  ChangeCh(e.string, '\\', '/');
+#endif
                   f.Printf("export %s := %s\n", e.name, e.string);
+#if defined(__WIN32__)
+                  ChangeCh(e.string, '/', '\\');
+#endif
                }
                f.Puts("\n");
             }
 
+            f.Puts("# PREFIXES AND EXTENSIONS\n");
+            f.Puts("EC := .ec\n");
+            f.Puts("S := .sym\n");
+            f.Puts("I := .imp\n");
+            f.Puts("B := .bowl\n");
+            f.Puts("C := .c\n");
+            f.Printf("O := .%s\n", compiler.objectFileExt ? compiler.objectFileExt  : "o");
+            f.Printf("A := .%s\n", compiler.staticLibFileExt ? compiler.staticLibFileExt  : "a");
+            f.Printf("SO := .%s\n", compiler.sharedLibFileExt ? compiler.sharedLibFileExt : "$(if $(WINDOWS_TARGET),dll,$(if $(OSX_TARGET),dylib,so))");
+            f.Printf("E := %s%s\n", compiler.executableFileExt ? "." : "",
+                  compiler.executableFileExt ? compiler.executableFileExt : "$(if $(WINDOWS_TARGET),.exe,)");
+            f.Puts("OUT := $(if $(STATIC_LIBRARY_TARGET),$(A),$(if $(SHARED_LIBRARY_TARGET),$(SO)$(VER),$(if $(EXECUTABLE_TARGET),$(E),.x)))\n");
+            f.Puts("LP := $(if $(WINDOWS_TARGET),$(if $(STATIC_LIBRARY_TARGET),lib,),lib)\n");
+            f.Puts("HOST_E := $(if $(WINDOWS_HOST),.exe,)\n");
+            f.Puts("HOST_SO := $(if $(WINDOWS_HOST),.dll,$(if $(OSX_HOST),.dylib,.so))\n");
+            f.Puts("HOST_LP := $(if $(WINDOWS_HOST),$(if $(STATIC_LIBRARY_TARGET),lib,),lib)\n");
+            f.Puts(".SUFFIXES: .c .ec .sym .imp .bowl $(O) $(A)\n");
+
             f.Puts("# TOOLCHAIN\n");
             f.Puts("\n");
 
+            f.Puts("# OPTIONS\n");
+            if(compiler.resourcesDotEar)
+            {
+               f.Puts("ifndef STATIC_LIBRARY_TARGET\n");
+               f.Puts("   USE_RESOURCES_EAR := defined\n");
+               f.Puts("endif\n");
+            }
+            if(compiler.noStripTarget)
+               f.Puts("NOSTRIP := defined");
+
+            f.Puts("\n");
+
+            f.Puts("# EXTENSIONS\n");
+            if(compiler.outputFileExt)
+               f.Printf("OUT := %s\n", compiler.outputFileExt);
+            else
+               f.Puts("OUT := $(if $(STATIC_LIBRARY_TARGET),$(A),$(if $(SHARED_LIBRARY_TARGET),$(SO)$(VER),$(if $(EXECUTABLE_TARGET),$(E),.x)))\n");
+
             if(gnuToolchainPrefix && gnuToolchainPrefix[0])
             {
                f.Printf("GCC_PREFIX := %s\n", gnuToolchainPrefix);
@@ -2539,9 +2580,9 @@ private:
             f.Printf("CXX := $(CCACHE_COMPILE)$(DISTCC_COMPILE)$(GCC_PREFIX)%s$(_SYSROOT)$(if $(GCC_CXX_FLAGS),$(space)$(GCC_CXX_FLAGS),)\n", compiler.cxxCommand);
             if(eC)
             {
-               f.Printf("ECP := $(if $(ECP_DEBUG),ide -debug-start \"$(ECERE_SDK_SRC)/compiler/ecp/ecp.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.ecpCommand);
-               f.Printf("ECC := $(if $(ECC_DEBUG),ide -debug-start \"$(ECERE_SDK_SRC)/compiler/ecc/ecc.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.eccCommand);
-               f.Printf("ECS := $(if $(ECS_DEBUG),ide -debug-start \"$(ECERE_SDK_SRC)/compiler/ecs/ecs.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(OUTPUT_POT), -outputpot,)$(if $(DISABLED_POOLING), -disabled-pooling,)\n", compiler.ecsCommand);
+               f.Printf("ECP := $(if $(ECP_DEBUG),ecere-ide -debug-start \"$(ECERE_SDK_SRC)/compiler/ecp/ecp.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.ecpCommand);
+               f.Printf("ECC := $(if $(ECC_DEBUG),ecere-ide -debug-start \"$(ECERE_SDK_SRC)/compiler/ecc/ecc.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(GCC_CC_FLAGS),$(space)$(GCC_CC_FLAGS),)\n", compiler.eccCommand);
+               f.Printf("ECS := $(if $(ECS_DEBUG),ecere-ide -debug-start \"$(ECERE_SDK_SRC)/compiler/ecs/ecs.epj\" -debug-work-dir \"${CURDIR}\" -@,%s)$(if $(CROSS_TARGET), -t $(TARGET_PLATFORM),)$(if $(OUTPUT_POT), -outputpot,)$(if $(DISABLED_POOLING), -disabled-pooling,)\n", compiler.ecsCommand);
             }
             else
             {
@@ -2552,16 +2593,25 @@ private:
             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.Printf("LD := ");
+            if(compiler.ldCommand && compiler.ldCommand[0])
+            {
+               f.Puts("$(GCC_PREFIX)");
+               f.Puts(compiler.ldCommand);
+            }
+            else
+               f.Puts("$(if $(CONTAINS_CXX),$(CXX),$(CC))");
+            f.Puts("$(_SYSROOT)$(if $(GCC_LD_FLAGS),$(space)$(GCC_LD_FLAGS),)\n");
+
+            f.Printf("AR := $(GCC_PREFIX)%s\n", compiler.arCommand);
             f.Puts("STRIP := $(GCC_PREFIX)strip\n");
             f.Puts("ifdef WINDOWS_TARGET\n");
             f.Puts("WINDRES := $(GCC_PREFIX)windres\n");
             f.Puts(" ifdef ARCH\n");
-            f.Puts("  ifeq \"$(ARCH)\" \"x32\"\n");
+            f.Puts("  ifeq ($(ARCH),x32)\n");
             f.Puts("WINDRES_FLAGS := -F pe-i386\n");
             f.Puts("  else\n");
-            f.Puts("   ifeq \"$(ARCH)\" \"x64\"\n");
+            f.Puts("   ifeq ($(ARCH),x64)\n");
             f.Puts("WINDRES_FLAGS := -F pe-x86-64\n");
             f.Puts("   endif\n");
             f.Puts("  endif\n");
@@ -2640,6 +2690,12 @@ private:
                OutputFlags(f, any, compiler.compilerFlags, inPlace);
                f.Puts("\n");
             }
+            if(compiler.cxxFlags && compiler.cxxFlags.count)
+            {
+               f.Puts("\nCXXFLAGS +=");
+               OutputFlags(f, any, compiler.cxxFlags, inPlace);
+               f.Puts("\n");
+            }
             if(compiler.linkerFlags && compiler.linkerFlags.count)
             {
                f.Puts("\nLDFLAGS +=");
@@ -2755,6 +2811,9 @@ private:
          f.Printf("MODULE := %s\n", fixedModuleName);
          f.Printf("VERSION := %s\n", property::moduleVersion);
          f.Printf("CONFIG := %s\n", fixedConfigName);
+         topNode.GenMakefilePrintNode(f, this, noPrint, null, null, config, &containsCXX);
+         if(containsCXX)
+            f.Puts("CONTAINS_CXX := defined\n");
          f.Puts("ifndef COMPILER\n" "COMPILER := default\n" "endif\n");
          f.Puts("\n");
 
@@ -2855,7 +2914,7 @@ private:
                      if(ifCount)
                         f.Puts("else\n");
                      ifCount++;
-                     f.Printf("ifeq \"$(TARGET_TYPE)\" \"%s\"\n", TargetTypeToMakefileVariable(type));
+                     f.Printf("ifeq ($(TARGET_TYPE),%s)\n", TargetTypeToMakefileVariable(type));
 
                      GetMakefileTargetFileName(type, target, config);
                      strcpy(temp, targetDir);
@@ -2976,7 +3035,7 @@ private:
             f.Puts("endif\n\n");
          }
 
-         numObjects = topNode.GenMakefilePrintNode(f, this, objects, namesInfo, listItems, config, &containsCXX);
+         numObjects = topNode.GenMakefilePrintNode(f, this, objects, namesInfo, listItems, config, null);
          if(numObjects)
             objectsParts = OutputFileList(f, "_OBJECTS", listItems, varStringLenDiffs, null);
          f.Printf("OBJECTS =%s%s%s%s\n",
@@ -3001,6 +3060,13 @@ private:
             resNode.GenMakefilePrintNode(f, this, resources, null, listItems, config, null);
          OutputFileList(f, "RESOURCES", listItems, varStringLenDiffs, null);
 
+         f.Puts("ifdef USE_RESOURCES_EAR\n");
+         f.Puts("RESOURCES_EAR = $(OBJ)resources.ear\n");
+         f.Puts("else\n");
+         f.Puts("RESOURCES_EAR = $(RESOURCES)\n");
+         f.Puts("endif\n");
+         f.Puts("\n");
+
          f.Puts("LIBS += $(SHAREDLIB) $(EXECUTABLE) $(LINKOPT)\n");
          f.Puts("\n");
          if((config && config.options && config.options.libraries) ||
@@ -3253,7 +3319,7 @@ private:
 
          f.Puts("objdir:\n");
          if(!relObjDir)
-            f.Puts("\t$(if $(wildcard $(OBJ)),,$(call mkdirq,$(OBJ)))\n");
+            f.Puts("\t$(if $(wildcard $(OBJ)),,$(call mkdir,$(OBJ)))\n");
          if(numCObjects)
          {
             f.Puts("\t$(if $(ECERE_SDK_SRC),$(if $(wildcard $(call escspace,$(ECERE_SDK_SRC)/crossplatform.mk)),,@$(call echo,Ecere SDK Source Warning: The value of ECERE_SDK_SRC is pointing to an incorrect ($(ECERE_SDK_SRC)) location.)),)\n");
@@ -3311,7 +3377,7 @@ private:
          if(!sameOrRelObjTargetDirs)
          {
             f.Puts("targetdir:\n");
-               f.Printf("\t$(if $(wildcard %s),,$(call mkdirq,%s))\n", targetDirExpNoSpaces, targetDirExpNoSpaces);
+               f.Printf("\t$(if $(wildcard %s),,$(call mkdir,%s))\n", targetDirExpNoSpaces, targetDirExpNoSpaces);
             f.Puts("\n");
          }
 
@@ -3319,7 +3385,7 @@ private:
          {
             // Main Module (Linking) for ECERE C modules
             f.Puts("$(OBJ)$(MODULE).main.ec: $(SYMBOLS) $(COBJECTS)\n");
-            f.Printf("\t@$(call rmq,$(OBJ)symbols.lst)\n");
+            f.Printf("\t@$(call rm,$(OBJ)symbols.lst)\n");
             f.Printf("\t@$(call touch,$(OBJ)symbols.lst)\n");
             OutputFileListActions(f, "SYMBOLS", eCsourcesParts, "$(OBJ)symbols.lst");
             OutputFileListActions(f, "IMPORTS", eCsourcesParts, "$(OBJ)symbols.lst");
@@ -3336,6 +3402,15 @@ private:
             f.Puts("\n");
          }
 
+         if(resNode.files && resNode.files.count && !noResources)
+         {
+            f.Puts("ifdef USE_RESOURCES_EAR\n");
+            f.Puts("$(RESOURCES_EAR): $(RESOURCES) | objdir\n");
+               resNode.GenMakefileAddResources(f, resNode.path, config, "RESOURCES_EAR");
+            f.Puts("endif\n");
+            f.Puts("\n");
+         }
+
          // *** Target ***
 
          // This would not rebuild the target on updated objects
@@ -3346,10 +3421,10 @@ private:
          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)%s $(RESOURCES) $(SYMBOLS) $(OBJECTS) | objdir%s\n",
+         f.Printf("$(TARGET): $(SOURCES)%s $(RESOURCES_EAR) $(SYMBOLS) $(OBJECTS) | objdir%s\n",
                rcSourcesParts ? " $(RCSOURCES)" : "", sameOrRelObjTargetDirs ? "" : " targetdir");
 
-         f.Printf("\t@$(call rmq,$(OBJ)objects.lst)\n");
+         f.Printf("\t@$(call rm,$(OBJ)objects.lst)\n");
          f.Printf("\t@$(call touch,$(OBJ)objects.lst)\n");
          OutputFileListActions(f, "_OBJECTS", objectsParts, "$(OBJ)objects.lst");
          if(rcSourcesParts)
@@ -3360,13 +3435,13 @@ private:
          }
          if(numCObjects)
          {
-            f.Printf("\t@$(call echo,$(OBJ)$(MODULE).main$(O)) >> $(OBJ)objects.lst\n");
+            f.Printf("\t$(call addtolistfile,$(OBJ)$(MODULE).main$(O),$(OBJ)objects.lst)\n");
             OutputFileListActions(f, "ECOBJECTS", eCsourcesParts, "$(OBJ)objects.lst");
          }
 
          f.Puts("ifndef STATIC_LIBRARY_TARGET\n");
 
-         f.Printf("\t$(%s) $(OFLAGS) @$(OBJ)objects.lst $(LIBS) -o $(TARGET) $(INSTALLNAME)\n", containsCXX ? "CXX" : "CC");
+         f.Puts("\t$(LD) $(OFLAGS) @$(OBJ)objects.lst $(LIBS) -o $(TARGET) $(INSTALLNAME)\n");
          if(!GetDebug(config))
          {
             f.Puts("ifndef NOSTRIP\n");
@@ -3380,14 +3455,18 @@ private:
                   f.Puts("\t$(UPX) $(UPXFLAGS) $(TARGET)\n");
                f.Puts("endif\n");
                f.Puts("else\n");
-               //f.Puts("ifneq \"$(TARGET_ARCH)\" \"x86_64\"\n");
+               //f.Puts("ifneq ($(TARGET_ARCH),x86_64)\n");
                   f.Puts("\t$(UPX) $(UPXFLAGS) $(TARGET)\n");
                //f.Puts("endif\n");
                f.Puts("endif\n");
             }
          }
          if(resNode.files && resNode.files.count && !noResources)
-            resNode.GenMakefileAddResources(f, resNode.path, config);
+         {
+            f.Puts("ifndef USE_RESOURCES_EAR\n");
+            resNode.GenMakefileAddResources(f, resNode.path, config, "TARGET");
+            f.Puts("endif\n");
+         }
          f.Puts("else\n");
          f.Puts("ifdef WINDOWS_HOST\n");
          f.Puts("\t$(AR) rcs $(TARGET) @$(OBJ)objects.lst $(LIBS)\n");
@@ -3544,17 +3623,17 @@ private:
          f.Printf("cleantarget: objdir%s\n", sameOrRelObjTargetDirs ? "" : " targetdir");
          if(numCObjects)
          {
-            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)");
-            f.Printf("\t$(call rmq,$(OBJ)symbols.lst)\n");
+            f.Printf("\t$(call rm,%s)\n", "$(OBJ)$(MODULE).main$(O) $(OBJ)$(MODULE).main.c $(OBJ)$(MODULE).main.ec $(OBJ)$(MODULE).main$(I) $(OBJ)$(MODULE).main$(S)");
+            f.Printf("\t$(call rm,$(OBJ)symbols.lst)\n");
          }
-         f.Printf("\t$(call rmq,$(OBJ)objects.lst)\n");
-         f.Puts("\t$(call rmq,$(TARGET))\n");
+         f.Printf("\t$(call rm,$(OBJ)objects.lst)\n");
+         f.Puts("\t$(call rm,$(TARGET))\n");
          f.Puts("ifdef SHARED_LIBRARY_TARGET\n");
          f.Puts("ifdef LINUX_TARGET\n");
          f.Puts("ifdef LINUX_HOST\n");
          // TODO?: support symlinks for longer version numbers
-         f.Puts("\t$(call rmq,$(OBJ)$(LP)$(MODULE)$(SO)$(basename $(VER)))\n");
-         f.Puts("\t$(call rmq,$(OBJ)$(LP)$(MODULE)$(SO))\n");
+         f.Puts("\t$(call rm,$(OBJ)$(LP)$(MODULE)$(SO)$(basename $(VER)))\n");
+         f.Puts("\t$(call rm,$(OBJ)$(LP)$(MODULE)$(SO))\n");
          f.Puts("endif\n");
          f.Puts("endif\n");
          f.Puts("endif\n");
@@ -3576,19 +3655,28 @@ private:
             OutputCleanActions(f, "IMPORTS", eCsourcesParts);
             OutputCleanActions(f, "SYMBOLS", eCsourcesParts);
          }
+         if(resNode.files && resNode.files.count && !noResources)
+         {
+            f.Puts("ifdef USE_RESOURCES_EAR\n");
+            f.Printf("\t$(call rm,$(RESOURCES_EAR))\n");
+            f.Puts("endif\n");
+         }
          f.Puts("\n");
 
          f.Puts("realclean: cleantarget\n");
-         f.Puts("\t$(call rmrq,$(OBJ))\n");
+         f.Puts("\t$(call rmr,$(OBJ))\n");
          if(!sameOrRelObjTargetDirs)
-            f.Printf("\t$(call rmdirq,%s)\n", targetDirExpNoSpaces);
+            f.Printf("\t$(call rmdir,%s)\n", targetDirExpNoSpaces);
          f.Puts("\n");
 
          f.Puts("distclean: cleantarget\n");
          if(!sameOrRelObjTargetDirs)
-            f.Printf("\t$(call rmdirq,%s)\n", targetDirExpNoSpaces);
+            f.Printf("\t$(call rmdir,%s)\n", targetDirExpNoSpaces);
          if(!relObjDir)
-            f.Puts("\t$(call rmrq,obj/)\n");
+            f.Puts("\t$(call rmr,obj/)\n");
+         f.Puts("\t$(call rmr,.configs/)\n");
+         f.Puts("\t$(call rm,*.ews)\n");
+         f.Puts("\t$(call rm,*.Makefile)\n");
 
          delete f;
 
@@ -4594,7 +4682,7 @@ Project LoadProject(const char * filePath, const char * activeConfigName)
       project = LegacyBinaryLoadProject(f, filePath);
       if(!project)
       {
-         JSONParser parser { f = f };
+         ECONParser parser { f = f };
          /*JSONResult result = */parser.GetObject(class(Project), &project);
          if(project)
          {
@@ -4677,7 +4765,7 @@ Project LoadProject(const char * filePath, const char * activeConfigName)
    return project;
 }
 
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
 static GccVersionInfo GetGccVersionInfo(CompilerConfig compiler, const String compilerCommand)
 {
    GccVersionInfo result = unknown;