From 31d7f922ad8785126148684591267f1d05db2e5a Mon Sep 17 00:00:00 2001 From: Lou Garczynski Date: Sat, 6 Jun 2026 16:16:25 +0000 Subject: [PATCH] Fix execute_code failing on the BOM-only CodeDom error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Mono, mcs emits a stray UTF-8 BOM line on stdout. CodeDom can't parse it, so it fabricates an error that has no error number and whose text is just the BOM, then refuses to return the compiled assembly — even though mcs exited 0 and wrote the DLL. Users see this as a spurious "Line 1: " compilation failure (#1186). Compile to a temp DLL on disk instead of in-memory, skip the content-less BOM "error" while still surfacing real compiler errors, and Assembly.Load the DLL that mcs actually produced. The temp DLL is cleaned up in a finally block. --- MCPForUnity/Editor/Tools/ExecuteCode.cs | 50 +++++++++++++++++++------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/MCPForUnity/Editor/Tools/ExecuteCode.cs b/MCPForUnity/Editor/Tools/ExecuteCode.cs index 34d3f6679..e99d21f26 100644 --- a/MCPForUnity/Editor/Tools/ExecuteCode.cs +++ b/MCPForUnity/Editor/Tools/ExecuteCode.cs @@ -294,32 +294,58 @@ private static Assembly CodeDomCompile(string source, string[] assemblyPaths, ou } } + // Compile to a controlled DLL path instead of in-memory. mcs prints a stray BOM line on + // stdout that Mono's CodeDom can't parse, so it fabricates a bogus error (no error number, + // text is just the BOM) and refuses to surface the assembly — even though mcs exits 0 and + // wrote the DLL. We skip that bogus error and Assembly.Load the produced DLL ourselves. + string outputAssemblyPath = Path.Combine(Path.GetTempPath(), $"mcp-codedom-{Guid.NewGuid():N}.dll"); using (var provider = new CSharpCodeProvider()) { var parameters = new CompilerParameters { - GenerateInMemory = true, + GenerateInMemory = false, + OutputAssembly = outputAssemblyPath, GenerateExecutable = false, TreatWarningsAsErrors = false, CompilerOptions = "@\"" + responseFilePath + "\"", }; - var results = provider.CompileAssemblyFromSource(parameters, source); - - if (results.Errors.HasErrors) + try { + var results = provider.CompileAssemblyFromSource(parameters, source); + + bool hasRealErrors = false; foreach (CompilerError error in results.Errors) { - if (!error.IsWarning) - { - int userLine = Math.Max(1, error.Line - WrapperLineOffset); - errors.Add($"Line {userLine}: {error.ErrorText}"); - } + if (error.IsWarning) + continue; + + // The bogus BOM "error": no error number, text is just the BOM/whitespace. + string text = error.ErrorText?.Trim('', ' ', '\t', '\r', '\n'); + if (string.IsNullOrEmpty(error.ErrorNumber) && string.IsNullOrEmpty(text)) + continue; + + hasRealErrors = true; + int userLine = Math.Max(1, error.Line - WrapperLineOffset); + errors.Add($"Line {userLine}: {error.ErrorText}"); } - return null; - } - return results.CompiledAssembly; + if (hasRealErrors) + return null; + + if (!File.Exists(outputAssemblyPath)) + { + errors.Add("CodeDom reported success but produced no assembly."); + return null; + } + + return Assembly.Load(File.ReadAllBytes(outputAssemblyPath)); + } + finally + { + try { if (File.Exists(outputAssemblyPath)) File.Delete(outputAssemblyPath); } + catch { /* best effort */ } + } } } finally