ide: fix specifying all file extensions (output file extension for emscripten) inside...
[sdk] / ide / src / project / Project.ec
index 92bd356..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
 
@@ -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);
    }
 }
 
@@ -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;
@@ -690,10 +673,10 @@ CompilerConfig GetCompilerConfig()
 
 int GetBitDepth()
 {
-#ifdef MAKEFILE_GENERATOR
-   return 0; // todo: improve this somehow? add bit depth command line option?
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+   return ide.workspace ? ide.workspace.bitDepth : 0;
 #else
-   return ide.workspace.bitDepth;
+   return 0; // todo: improve this somehow? add bit depth command line option?
 #endif
 }
 
@@ -881,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 };
@@ -943,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);
 
@@ -959,7 +942,7 @@ private:
          }
          return true;
       }
-      return true;
+      return !ide.destroyed;
    }
 
 #endif
@@ -1190,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;
@@ -1199,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;
@@ -1208,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)
       {
@@ -1263,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");
@@ -1280,7 +1270,13 @@ private:
             }
             break;
          case staticLibrary:
-            strcat(string, ".a");
+            if(compiler.staticLibFileExt)
+            {
+               strcat(string, ".");
+               strcat(string, compiler.staticLibFileExt);
+            }
+            else
+               strcat(string, ".a");
             break;
       }
    }
@@ -1357,7 +1353,7 @@ private:
       sprintf(string, "%s%s%s.Makefile", projectName, config ? "-" : "", config ? config.name : "");
    }
 
-#ifndef MAKEFILE_GENERATOR
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
    ProjectNode GetObjectFileNode(const char * filePath, const char * objectFileExt)
    {
       ProjectNode node = null;
@@ -1566,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);
@@ -2090,7 +2086,7 @@ private:
       }
    }
 
-   bool Build(BuildType buildType, List<ProjectNode> onlyNodes, CompilerConfig compiler, ProjectConfig config, int bitDepth, bool justPrint, bool raw, SingleFileCompileMode mode)
+   bool Build(BuildType buildType, List<ProjectNode> onlyNodes, CompilerConfig compiler, ProjectConfig config, int bitDepth, BuildOutputMode outputMode, SingleFileCompileMode mode)
    {
       bool result = false;
       DualPipe f;
@@ -2110,7 +2106,6 @@ private:
       char command[MAX_F_STRING*4];
       char * compilerName = CopyString(compiler.name);
       Map<String, NameCollisionInfo> cfgNameCollisions;
-      const char * objFileExt = strcmp(compiler.objectFileExt, objectDefaultFileExt) != 0 ? compiler.objectFileExt : null;
 
       delete lastBuildConfigName;
       lastBuildConfigName = CopyString(config ? config.name : "Common");
@@ -2149,17 +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%s%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,
-                     objFileExt ? " O=." : "", objFileExt ? objFileExt : "",
-                     topNode.path, justPrint ? " -n" : "", makeFilePath);
-               if(justPrint || raw)
+                     topNode.path, outputMode == verbose ? " V=1" : "", outputMode == justPrint ? " -n" : "", makeFilePath);
+               if(outputMode != normal)
                   ide.outputView.buildBox.Logf("%s\n", command);
                Execute(command);
             }
@@ -2187,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 || raw)
+         if(outputMode != normal)
             ide.outputView.buildBox.Logf("%s\n", command);
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true/*, input = true*/ }, command)))
          {
@@ -2209,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%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
@@ -2225,15 +2218,15 @@ private:
                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,
-               objFileExt ? " O=." : "", objFileExt ? objFileExt : "",
+               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 || raw)
+               (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)))
@@ -2243,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())
                {
@@ -2252,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;
@@ -2270,14 +2263,14 @@ private:
                if(found)
                   result = true;
             }
-            else if(justPrint || raw)
+            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);
@@ -2293,7 +2286,7 @@ private:
       return result;
    }
 
-   void Clean(CompilerConfig compiler, ProjectConfig config, int bitDepth, CleanType cleanType, bool justPrint, bool raw)
+   void Clean(CompilerConfig compiler, ProjectConfig config, int bitDepth, CleanType cleanType, BuildOutputMode outputMode)
    {
       char makeFile[MAX_LOCATION];
       char makeFilePath[MAX_LOCATION];
@@ -2303,7 +2296,6 @@ private:
       PathBackup pathBackup { };
       bool crossCompiling = (compiler.targetPlatform != __runtimePlatform);
       const char * targetPlatform = crossCompiling ? (char *)compiler.targetPlatform : "";
-      const char * objFileExt = strcmp(compiler.objectFileExt, objectDefaultFileExt) ? compiler.objectFileExt : null;
 
       compilerName = CopyString(compiler.name);
       CamelCase(compilerName);
@@ -2321,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 || raw)
+         if(outputMode != normal)
             ide.outputView.buildBox.Logf("%s\n", command);
          if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
@@ -2338,22 +2330,21 @@ private:
       {
          char cfDir[MAX_LOCATION];
          GetIDECompilerConfigsDir(cfDir, true, true);
-         sprintf(command, "%s CF_DIR=\"%s\"%s%s%s%s COMPILER=%s%s%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,
-               objFileExt ? " O=." : "", objFileExt ? objFileExt : "",
                cleanType == realClean ? "real" : "", cleanType == cleanTarget ? "target" : "",
-               topNode.path, justPrint ? " -n": "", makeFilePath);
-         if(justPrint || raw)
+               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 || raw)
+            if(outputMode != normal)
                ProcessPipeOutputRaw(f);
             else
                ProcessCleanPipeOutput(f, compiler, config);
@@ -2368,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];
@@ -2376,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);
@@ -2404,6 +2396,8 @@ private:
          Execute(prefixedTarget);
          delete prefixedTarget;
       }
+      else if(shellOpen)
+         ShellOpen(target);
       else
          Execute(target);
 
@@ -2414,9 +2408,9 @@ private:
       delete target;
    }
 
-   bool Compile(List<ProjectNode> nodes, CompilerConfig compiler, ProjectConfig config, int bitDepth, bool justPrint, bool raw, 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, raw, mode);
+      return Build(build, nodes, compiler, config, bitDepth, outputMode, mode);
    }
 #endif
 
@@ -2528,9 +2522,39 @@ private:
                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);
@@ -2556,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
             {
@@ -2569,8 +2593,16 @@ private:
             f.Printf("EAR := %s\n", compiler.earCommand);
 
             f.Puts("AS := $(GCC_PREFIX)as\n");
-            f.Printf("LD := $(GCC_PREFIX)%s$(_SYSROOT)$(if $(GCC_LD_FLAGS),$(space)$(GCC_LD_FLAGS),)\n",
-                  compiler.ldCommand && compiler.ldCommand[0] ? compiler.ldCommand : "$(if $(CONTAINS_CXX),$(CXX),$(CC))");
+            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");
@@ -2658,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 +=");
@@ -3022,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) ||
@@ -3357,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
@@ -3367,7 +3421,7 @@ 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 rm,$(OBJ)objects.lst)\n");
@@ -3381,7 +3435,7 @@ 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");
          }
 
@@ -3408,7 +3462,11 @@ private:
             }
          }
          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");
@@ -3597,6 +3655,12 @@ 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");
@@ -3610,6 +3674,9 @@ private:
             f.Printf("\t$(call rmdir,%s)\n", targetDirExpNoSpaces);
          if(!relObjDir)
             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;
 
@@ -4615,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)
          {
@@ -4698,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;