buildsystem,epj2make,ide; remove .main.* files when cleaning the target of a project...
[sdk] / ide / src / project / Project.ec
index ff08f85..b5e7863 100644 (file)
@@ -632,7 +632,7 @@ enum LineOutputMethod { inPlace, newLine, lineEach };
 enum StringOutputMethod { asIs, escape, escapePath};
 
 enum ToolchainFlag { any, _D, _I, _isystem, _Wl, _L/*, _Wl-rpath*/ };
-String flagNames[ToolchainFlag] = { "", "-D", "-I", "-isystem ", "-Wl,", "-Wl,--library-path="/*, "-Wl,-rpath "*/ };
+String flagNames[ToolchainFlag] = { "", "-D", "-I", "-isystem ", "-Wl,", /*"-Wl,--library-path="*/"-L"/*, "-Wl,-rpath "*/ };
 void OutputFlags(File f, ToolchainFlag flag, Array<String> list, LineOutputMethod lineMethod)
 {
    if(list.count)
@@ -890,7 +890,7 @@ private:
    String lastBuildConfigName;
    String lastBuildCompilerName;
 
-   Map<String, NameCollisionInfo> lastBuildNamesInfo;
+   Map<String, Map<String, NameCollisionInfo>> configsNameCollisions { };
 
 #ifndef MAKEFILE_GENERATOR
    FileMonitor fileMonitor
@@ -997,7 +997,9 @@ private:
       delete name;
       delete lastBuildConfigName;
       delete lastBuildCompilerName;
-      if(lastBuildNamesInfo) { lastBuildNamesInfo.Free(); delete lastBuildNamesInfo; }
+      for(map : configsNameCollisions)
+         map.Free();
+      configsNameCollisions.Free();
    }
 
    ~Project()
@@ -1059,9 +1061,13 @@ private:
    ProjectNode FindNodeByObjectFileName(char * fileName, IntermediateFileType type, bool dotMain, ProjectConfig config)
    {
       ProjectNode result;
-      if(!lastBuildNamesInfo)
+      char * cfgName;
+      if(!config)
+         config = this.config;
+      cfgName = config ? config.name : "";
+      if(!configsNameCollisions[cfgName])
          ProjectLoadLastBuildNamesInfo(this, config);
-      result = topNode.FindByObjectFileName(fileName, type, dotMain, lastBuildNamesInfo);
+      result = topNode.FindByObjectFileName(fileName, type, dotMain, configsNameCollisions[cfgName]);
       return result;
    }
 
@@ -1244,63 +1250,6 @@ private:
    }
 #endif
 
-   // This method is only called from Debugger, should be moved to Debugger class?
-#ifndef MAKEFILE_GENERATOR
-   bool GetRelativePath(char * filePath, char * relativePath)
-   {
-      ProjectNode node;
-      char moduleName[MAX_FILENAME];
-      GetLastDirectory(filePath, moduleName);
-      // try with workspace dir first?
-      if((node = topNode.Find(moduleName, false)))
-      {
-         strcpy(relativePath, strcmp(node.path, ".") ? node.path : "");
-         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;
-            int bitDepth = ide.debugger.bitDepth;
-            // 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, bitDepth);
-            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(relativePath, objDir);
-            *sl = '.';
-            PathCatSlash(relativePath, moduleName);
-            return true;
-         }
-      }
-      // WARNING: On failure, relative path is uninitialized
-      return false;   
-   }
-#endif
-
    void CatTargetFileName(char * string, CompilerConfig compiler, ProjectConfig config)
    {
       TargetTypes targetType = GetTargetType(config);
@@ -1437,10 +1386,26 @@ private:
 
    void ModifiedAllConfigs(bool making, bool compiling, bool linking, bool symbolGen)
    {
+      Map<String, NameCollisionInfo> cfgNameCollision = configsNameCollisions[""];
+      if(cfgNameCollision)
+      {
+         cfgNameCollision.Free();
+         delete cfgNameCollision;
+         configsNameCollisions[""] = null;
+      }
       for(cfg : configurations)
       {
          if(making)
+         {
             cfg.makingModified = true;
+            cfgNameCollision = configsNameCollisions[cfg.name];
+            if(cfgNameCollision)
+            {
+               cfgNameCollision.Free();
+               delete cfgNameCollision;
+               configsNameCollisions[cfg.name] = null;
+            }
+         }
          if(compiling)
             cfg.compilingModified = true;
          if(linking)
@@ -1455,7 +1420,7 @@ private:
       }
    }
    
-   void RotateActiveConfig(bool forward)
+   void RotateActiveConfig(bool forward, bool syncAllProjects)
    {
       if(configurations.first && configurations.last != configurations.first)
       {
@@ -1475,10 +1440,15 @@ private:
                cfg.Prev();
          }
 
-         property::config = cfg.data;
-         ide.UpdateToolBarActiveConfigs(true);
-         ide.workspace.modified = true;
-         ide.projectView.Update(null);
+         if(syncAllProjects)
+            ide.workspace.SelectActiveConfig(cfg.data.name);
+         else
+         {
+            property::config = cfg.data;
+            ide.UpdateToolBarActiveConfigs(true);
+            ide.workspace.modified = true;
+            ide.projectView.Update(null);
+         }
       }
    }
 
@@ -1517,7 +1487,7 @@ private:
       return !ide.projectView.stopBuild;
    }
 
-   bool ProcessBuildPipeOutput(DualPipe f, DirExpression objDirExp, bool isARun, List<ProjectNode> onlyNodes,
+   bool ProcessBuildPipeOutput(DualPipe f, DirExpression objDirExp, BuildType buildType, List<ProjectNode> onlyNodes,
       CompilerConfig compiler, ProjectConfig config, int bitDepth)
    {
       char line[65536];
@@ -1543,6 +1513,7 @@ private:
       DynamicString strip { };
       DynamicString ar { };
       DynamicString windres { };
+
       /*
       if(bitDepth == 64 && compiler.targetPlatform == win32) 
          gnuToolchainPrefix = "x86_64-w64-mingw32-";
@@ -1697,6 +1668,14 @@ private:
                   if(module)
                   {
                      byte * tokens[1];
+                     char * dashF = strstr(module, "-F ");
+                     if(dashF)
+                     {
+                        dashF+= 3;
+                        while(*dashF && *dashF != ' ') dashF++;
+                        while(*dashF && *dashF == ' ') dashF++;
+                        module = dashF;
+                     }
                      Tokenize(module, 1, tokens, (BackSlashEscaping)true); // fix #139
                      GetLastDirectory(module, moduleName);
                      ide.outputView.buildBox.Logf("%s\n", moduleName);
@@ -1772,7 +1751,7 @@ private:
                            else
                            {
                               numErrors++;
-                              message = $"Linker Error";
+                              message = $"Linker Error";
                            }
                         }
                         else if(linking && (!strcmp(ext, "") || !strcmp(ext, "exe")))
@@ -1940,14 +1919,14 @@ private:
          ide.outputView.buildBox.Logf($"\nBuild cancelled by user.\n", line);
          f.Terminate();
       }
-      else if(loggedALine || !isARun)
+      else if(loggedALine || buildType != run)
       {
          if(f.GetExitCode() && !numErrors)
          {
             bool result = f.GetLine(line, sizeof(line)-1);
             ide.outputView.buildBox.Logf($"Fatal Error: child process terminated unexpectedly\n");
          }
-         else
+         else if(buildType != install)
          {
             if(!onlyNodes)
             {
@@ -1979,6 +1958,7 @@ private:
       delete cxx;
       delete strip;
       delete ar;
+      delete windres;
 
       return numErrors == 0 && !ide.projectView.stopBuild;
    }
@@ -2019,7 +1999,7 @@ private:
       }
    }
 
-   bool Build(bool isARun, 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, bool justPrint, SingleFileCompileMode mode)
    {
       bool result = false;
       DualPipe f;
@@ -2038,12 +2018,14 @@ private:
       int numJobs = compiler.numJobs;
       char command[MAX_F_STRING*4];
       char * compilerName = CopyString(compiler.name);
+      Map<String, NameCollisionInfo> cfgNameCollisions;
 
       delete lastBuildConfigName;
       lastBuildConfigName = CopyString(config ? config.name : "Common");
       delete lastBuildCompilerName;
       lastBuildCompilerName = CopyString(compiler.name);
       ProjectLoadLastBuildNamesInfo(this, config);
+      cfgNameCollisions = configsNameCollisions[config ? config.name : ""];
 
       CamelCase(compilerName);
 
@@ -2057,7 +2039,9 @@ private:
       PathCatSlash(makeFilePath, makeFile);
 
       // TODO: TEST ON UNIX IF \" around makeTarget is ok
-      if(onlyNodes)
+      if(buildType == install)
+         makeTargets.concat(" install");
+      else if(onlyNodes)
       {
          if(compiler.type.isVC)
          {
@@ -2096,8 +2080,8 @@ private:
                else
                {
                   if(!eC_Debug)
-                     node.DeleteIntermediateFiles(compiler, config, bitDepth, lastBuildNamesInfo, mode == cObject ? true : false);
-                  node.GetTargets(config, lastBuildNamesInfo, objDirExp.dir, makeTargets);
+                     node.DeleteIntermediateFiles(compiler, config, bitDepth, cfgNameCollisions, mode == cObject ? true : false);
+                  node.GetTargets(config, cfgNameCollisions, objDirExp.dir, makeTargets);
                }
             }
          }
@@ -2114,7 +2098,7 @@ private:
          sprintf(command, "%s /useenv /nologo /logcommands %s.sln %s|Win32", compiler.makeCommand, name, config.name);
          if(justPrint)
             ide.outputView.buildBox.Logf("%s\n", command);
-         if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
+         if((f = DualPipeOpen(PipeOpenMode { output = true, error = true/*, input = true*/ }, command)))
          {
             ProcessPipeOutputRaw(f);
             delete f;
@@ -2130,7 +2114,12 @@ private:
       {
          char cfDir[MAX_LOCATION];
          GetIDECompilerConfigsDir(cfDir, true, true);
-         sprintf(command, "%s %sCF_DIR=\"%s\"%s%s%s%s%s COMPILER=%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-j%d %s%s%s -C \"%s\"%s -f \"%s\"",
+#if defined(__WIN32__)
+               "",
+#else
+               buildType == install ? "pkexec --user root " : "",
+#endif
                compiler.makeCommand,
                mode == debugPrecompile ? "ECP_DEBUG=y " : mode == debugCompile ? "ECC_DEBUG=y " : mode == debugGenerateSymbols ? "ECS_DEBUG=y " : "",
                cfDir,
@@ -2138,6 +2127,7 @@ private:
                targetPlatform,
                bitDepth ? " ARCH=" : "",
                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 " : "", numJobs,
                (compiler.ccacheEnabled && !eC_Debug) ? "CCACHE=y " : "",
@@ -2183,7 +2173,7 @@ private:
             else if(justPrint)
                result = ProcessPipeOutputRaw(f);
             else
-               result = ProcessBuildPipeOutput(f, objDirExp, isARun, onlyNodes, compiler, config, bitDepth);
+               result = ProcessBuildPipeOutput(f, objDirExp, buildType, onlyNodes, compiler, config, bitDepth);
             delete f;
             if(error)
                ide.outputView.buildBox.Logf("%s\n", command);
@@ -2256,7 +2246,7 @@ private:
                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)))
+         if((f = DualPipeOpen(PipeOpenMode { output = true, error = true, input = true }, command)))
          {
             ide.outputView.buildBox.Tellf($"Deleting %s%s...",
                   cleanType == realClean ? $"intermediate objects directory" : $"target",
@@ -2301,11 +2291,10 @@ private:
       }
       else
          ChangeWorkingDir(topNode.path);
-      // ChangeWorkingDir(topNode.path);
       SetPath(true, compiler, config, bitDepth);
       if(executableLauncher)
       {
-         char * prefixedTarget = new char[strlen(executableLauncher) + strlen(target) + 2];
+         char * prefixedTarget = new char[strlen(executableLauncher) + strlen(target) + 8];
          prefixedTarget[0] = '\0';
          strcat(prefixedTarget, executableLauncher);
          strcat(prefixedTarget, " ");
@@ -2325,7 +2314,7 @@ private:
 
    bool Compile(List<ProjectNode> nodes, CompilerConfig compiler, ProjectConfig config, int bitDepth, bool justPrint, SingleFileCompileMode mode)
    {
-      return Build(false, nodes, compiler, config, bitDepth, justPrint, mode);
+      return Build(build, nodes, compiler, config, bitDepth, justPrint, mode);
    }
 #endif
 
@@ -2464,7 +2453,7 @@ private:
             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\" -debug-work-dir \"${CURDIR}\" -@,%s)\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),)\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,)\n", compiler.ecsCommand);
+            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("EAR := %s\n", compiler.earCommand);
 
             f.Puts("AS := $(GCC_PREFIX)as\n");
@@ -3020,7 +3009,7 @@ private:
                         f.Puts("\n");
                      }
 
-                     if((configPlatformOptions && configPlatformOptions.options.libraries))
+                     if(configPlatformOptions && configPlatformOptions.options.libraries)
                      {
                         if(configPlatformOptions.options.libraries.count)
                         {
@@ -3147,7 +3136,7 @@ private:
          if(!relObjDir)
             f.Puts("\t$(if $(wildcard $(OBJ)),,$(call mkdirq,$(OBJ)))\n");
 
-            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)/crossplatform.mk) location.)),)\n");
+            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");
             f.Puts("\t$(if $(ECERE_SDK_SRC),,$(if $(ECP_DEBUG)$(ECC_DEBUG)$(ECS_DEBUG),@$(call echo,ECC Debug Warning: Please define ECERE_SDK_SRC before using ECP_DEBUG, ECC_DEBUG or ECS_DEBUG),))\n");
          //f.Puts("# PRE-BUILD COMMANDS\n");
          if(options && options.prebuildCommands)
@@ -3210,7 +3199,7 @@ private:
             // Main Module (Linking) for ECERE C modules
             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 $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols %s -o $@\n",
+            f.Printf("\t$(ECS)%s $(ARCH_FLAGS) $(ECSLIBOPT) $(SYMBOLS) $(IMPORTS) -symbols %s -o $(call escspace,$@)\n",
                GetConsole(config) ? " -console" : "", objDirExpNoSpaces);
             f.Puts("\n");
             // Main Module (Linking) for ECERE C modules
@@ -3218,7 +3207,7 @@ private:
             f.Puts("\t$(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS)"
                   " -c $(OBJ)$(MODULE).main.ec -o $(OBJ)$(MODULE).main.sym -symbols $(OBJ)\n");
             f.Puts("\t$(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY)"
-                  " -c $(OBJ)$(MODULE).main.ec -o $@ -symbols $(OBJ)\n");
+                  " -c $(OBJ)$(MODULE).main.ec -o $(call escspace,$@) -symbols $(OBJ)\n");
             f.Puts("\n");
          }
 
@@ -3266,8 +3255,10 @@ 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("\t$(UPX) $(UPXFLAGS) $(TARGET)\n");
                f.Puts("endif\n");
+               f.Puts("endif\n");
             }
          }
          if(resNode.files && resNode.files.count && !noResources)
@@ -3334,6 +3325,74 @@ private:
          }
          f.Puts("\n");
 
+         test = false;
+         if(platforms || (config && config.platforms))
+         {
+            for(platform = (Platform)1; platform < Platform::enumSize; platform++)
+            {
+               PlatformOptions projectPOs, configPOs;
+               MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
+
+               if((projectPOs && projectPOs.options.installCommands && projectPOs.options.installCommands.count) ||
+                     (configPOs && configPOs.options.installCommands && configPOs.options.installCommands.count))
+               {
+                  test = true;
+                  break;
+               }
+            }
+         }
+         if(test || (options && options.installCommands) ||
+               (config && config.options && config.options.installCommands))
+         {
+            f.Puts("install:\n");
+            if(options && options.installCommands)
+            {
+               for(s : options.installCommands)
+                  if(s && s[0]) f.Printf("\t%s\n", s);
+            }
+            if(config && config.options && config.options.installCommands)
+            {
+               for(s : config.options.installCommands)
+                  if(s && s[0]) f.Printf("\t%s\n", s);
+            }
+            if(platforms || (config && config.platforms))
+            {
+               ifCount = 0;
+               for(platform = (Platform)1; platform < Platform::enumSize; platform++)
+               {
+                  PlatformOptions projectPOs, configPOs;
+                  MatchProjectAndConfigPlatformOptions(config, platform, &projectPOs, &configPOs);
+
+                  if((projectPOs && projectPOs.options.installCommands && projectPOs.options.installCommands.count) ||
+                        (configPOs && configPOs.options.installCommands && configPOs.options.installCommands.count))
+                  {
+                     if(ifCount)
+                        f.Puts("else\n");
+                     ifCount++;
+                     f.Printf("ifdef %s\n", PlatformToMakefileTargetVariable(platform));
+
+                     if(projectPOs && projectPOs.options.installCommands && projectPOs.options.installCommands.count)
+                     {
+                        for(s : projectPOs.options.installCommands)
+                           if(s && s[0]) f.Printf("\t%s\n", s);
+                     }
+                     if(configPOs && configPOs.options.installCommands && configPOs.options.installCommands.count)
+                     {
+                        for(s : configPOs.options.installCommands)
+                           if(s && s[0]) f.Printf("\t%s\n", s);
+                     }
+                  }
+               }
+               if(ifCount)
+               {
+                  int c;
+                  for(c = 0; c < ifCount; c++)
+                     f.Puts("endif\n");
+               }
+            }
+            f.Puts("\n");
+         }
+
          f.Puts("# SYMBOL RULES\n");
          f.Puts("\n");
 
@@ -3355,6 +3414,8 @@ private:
 
          f.Printf("cleantarget: objdir%s\n", sameOrRelObjTargetDirs ? "" : " targetdir");
          f.Puts("\t$(call rmq,$(TARGET))\n");
+         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.Puts("ifdef SHARED_LIBRARY_TARGET\n");
          f.Puts("ifdef LINUX_TARGET\n");
          f.Puts("ifdef LINUX_HOST\n");
@@ -3377,7 +3438,6 @@ private:
          }
          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)");
             OutputCleanActions(f, "ECOBJECTS", eCsourcesParts);
             OutputCleanActions(f, "COBJECTS", eCsourcesParts);
             OutputCleanActions(f, "BOWLS", eCsourcesParts);
@@ -3476,7 +3536,7 @@ private:
       }
 
       // Execute it
-      if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
+      if((dep = DualPipeOpen(PipeOpenMode { output = true, error = true/*, input = true*/ }, command)))
       {
          char line[1024];
          bool result = true;
@@ -3509,7 +3569,7 @@ private:
          {
 #endif
             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 $@\n", extension);
+            f.Printf("\t$(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(OBJ)$(MODULE).main.%s -o $(call escspace,$@)\n", extension);
             f.Puts("\n");
 #if 0
          }
@@ -3570,13 +3630,15 @@ private:
 
 static inline void ProjectLoadLastBuildNamesInfo(Project prj, ProjectConfig cfg)
 {
-   if(prj.lastBuildNamesInfo)
+   char * cfgName = cfg ? cfg.name : "";
+   Map<String, NameCollisionInfo> cfgNameCollisions = prj.configsNameCollisions[cfgName];
+   if(cfgNameCollisions)
    {
-      prj.lastBuildNamesInfo.Free();
-      delete prj.lastBuildNamesInfo;
+      cfgNameCollisions.Free();
+      delete cfgNameCollisions;
    }
-   prj.lastBuildNamesInfo = { };
-   prj.topNode.GenMakefileGetNameCollisionInfo(prj.lastBuildNamesInfo, cfg);
+   prj.configsNameCollisions[cfgName] = cfgNameCollisions = { };
+   prj.topNode.GenMakefileGetNameCollisionInfo(cfgNameCollisions, cfg);
 }
 
 Project LegacyBinaryLoadProject(File f, char * filePath)