From 4e34897ac1f8e89dbf95e9c012555e020af46dd2 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 28 Jan 2024 02:16:42 +0200 Subject: [PATCH] Fixed BCS compiler error reporting --- Build/Compilers/BCC/bcc.cfg | 2 +- Build/Updater.ini | 6 +- Source/Core/Builder.csproj | 1 + Source/Core/Compilers/AccCompiler.cs | 189 +++++++++++++++------------ Source/Core/Compilers/BccCompiler.cs | 83 ++++++++++++ 5 files changed, 192 insertions(+), 89 deletions(-) create mode 100644 Source/Core/Compilers/BccCompiler.cs diff --git a/Build/Compilers/BCC/bcc.cfg b/Build/Compilers/BCC/bcc.cfg index ccac4682..5d1d733b 100755 --- a/Build/Compilers/BCC/bcc.cfg +++ b/Build/Compilers/BCC/bcc.cfg @@ -7,7 +7,7 @@ compilers // All others are the required files (the setting names do not matter) bcc { - interface = "AccCompiler"; + interface = "BccCompiler"; program = "bcc.exe"; zcommon = "zcommon.bcs"; std = "std.acs"; diff --git a/Build/Updater.ini b/Build/Updater.ini index 29e7b98a..734436a2 100755 --- a/Build/Updater.ini +++ b/Build/Updater.ini @@ -1,5 +1,5 @@ URL https://ultimatedoombuilder.github.io/files/ FileName Builder.exe -UpdateName UltimateDoomBuilder-r[REVNUM]-x86.7z -InstallerName UltimateDoomBuilder-Setup-R[REVNUM]-x86.exe -UpdaterName UDB_Updater-x86.7z \ No newline at end of file +UpdateName UltimateDoomBuilder-r[REVNUM]-x64.7z +InstallerName UltimateDoomBuilder-Setup-R[REVNUM]-x64.exe +UpdaterName UDB_Updater-x64.7z \ No newline at end of file diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj index 54cd5111..37576b03 100644 --- a/Source/Core/Builder.csproj +++ b/Source/Core/Builder.csproj @@ -141,6 +141,7 @@ --> + diff --git a/Source/Core/Compilers/AccCompiler.cs b/Source/Core/Compilers/AccCompiler.cs index 867d1a47..ac5b1b44 100755 --- a/Source/Core/Compilers/AccCompiler.cs +++ b/Source/Core/Compilers/AccCompiler.cs @@ -30,11 +30,17 @@ using CodeImp.DoomBuilder.ZDoom.Scripting; namespace CodeImp.DoomBuilder.Compilers { - internal sealed class AccCompiler : Compiler + internal class AccCompiler : Compiler { - #region ================== Constants - - private const string ACS_ERROR_FILE = "acs.err"; + #region ================== Internal classes + + protected class CompileContext { } + + #endregion + + #region ================== Constants + + private const string ACS_ERROR_FILE = "acs.err"; #endregion @@ -74,12 +80,102 @@ namespace CodeImp.DoomBuilder.Compilers #endregion #region ================== Methods + + protected virtual CompileContext OnBeforeProcessStart(ProcessStartInfo info) + { + return new CompileContext(); + } + + protected virtual void OnCheckError(HashSet includes, ProcessStartInfo processinfo, Process process, CompileContext context) + { + int line = 0; + + // Now find the error file + string errfile = Path.Combine(this.workingdir, ACS_ERROR_FILE); + if (File.Exists(errfile)) + { + try + { + // Regex to find error lines + Regex errlinematcher = new Regex(":[0-9]+: ", RegexOptions.Compiled | RegexOptions.CultureInvariant); + + // Read all lines + bool erroradded = false; //mxd + string[] errlines = File.ReadAllLines(errfile); + string temppath = this.tempdir.FullName + Path.DirectorySeparatorChar.ToString(); //mxd. Need trailing slash.. + while (line < errlines.Length) + { + // Check line + string linestr = errlines[line]; + Match match = errlinematcher.Match(linestr); + if (match.Success && (match.Index > 0)) + { + CompilerError err = new CompilerError(); + + // The match without spaces and semicolon is the line number + string linenr = match.Value.Replace(":", "").Trim(); + if (!int.TryParse(linenr, out err.linenumber)) + err.linenumber = CompilerError.NO_LINE_NUMBER; + else + err.linenumber--; + + // Everything before the match is the filename + err.filename = linestr.Substring(0, match.Index).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + + //mxd. Get rid of temp directory path + if (err.filename.StartsWith(temppath)) err.filename = err.filename.Replace(temppath, string.Empty); + + if (!Path.IsPathRooted(err.filename)) + { + //mxd. If the error is in an include file, try to find it in loaded resources + if (includes.Contains(err.filename)) + { + foreach (DataReader dr in General.Map.Data.Containers) + { + if (dr is DirectoryReader && dr.FileExists(err.filename)) + { + err.filename = Path.Combine(dr.Location.location, err.filename); + break; + } + } + } + else + { + // Add working directory to filename, so it could be recognized as map namespace lump in MapManager.CompileLump() + err.filename = Path.Combine(processinfo.WorkingDirectory, err.filename); + } + } + + // Everything after the match is the description + err.description = linestr.Substring(match.Index + match.Length).Trim(); + + // Report the error + ReportError(err); + erroradded = true; //mxd + } + + // Next line + line++; + } + + //mxd. Some ACC errors are not properly formatted. If that's the case, threat the whole acs.err as an error... + if (!erroradded && errlines.Length > 0) + { + ReportError(new CompilerError(string.Join(Environment.NewLine, errlines))); + } + } + catch (Exception e) + { + // Error reading errors (ironic, isn't it) + ReportError(new CompilerError("Failed to retrieve compiler error report. " + e.GetType().Name + ": " + e.Message)); + } + } + } // This runs the compiler public override bool Run() { Process process; - int line = 0; string sourcedir = Path.GetDirectoryName(sourcefile); // Preprocess the file @@ -174,6 +270,8 @@ namespace CodeImp.DoomBuilder.Compilers processinfo.UseShellExecute = true; processinfo.WindowStyle = ProcessWindowStyle.Hidden; processinfo.WorkingDirectory = this.workingdir; + + CompileContext context = OnBeforeProcessStart(processinfo); // Output info General.WriteLogLine("Running compiler..."); @@ -197,87 +295,8 @@ namespace CodeImp.DoomBuilder.Compilers TimeSpan deltatime = TimeSpan.FromTicks(process.ExitTime.Ticks - process.StartTime.Ticks); General.WriteLogLine("Compiler process has finished."); General.WriteLogLine("Compile time: " + deltatime.TotalSeconds.ToString("########0.00") + " seconds"); - - // Now find the error file - string errfile = Path.Combine(this.workingdir, ACS_ERROR_FILE); - if(File.Exists(errfile)) - { - try - { - // Regex to find error lines - Regex errlinematcher = new Regex(":[0-9]+: ", RegexOptions.Compiled | RegexOptions.CultureInvariant); - - // Read all lines - bool erroradded = false; //mxd - string[] errlines = File.ReadAllLines(errfile); - string temppath = this.tempdir.FullName + Path.DirectorySeparatorChar.ToString(); //mxd. Need trailing slash.. - while(line < errlines.Length) - { - // Check line - string linestr = errlines[line]; - Match match = errlinematcher.Match(linestr); - if(match.Success && (match.Index > 0)) - { - CompilerError err = new CompilerError(); - - // The match without spaces and semicolon is the line number - string linenr = match.Value.Replace(":", "").Trim(); - if(!int.TryParse(linenr, out err.linenumber)) - err.linenumber = CompilerError.NO_LINE_NUMBER; - else - err.linenumber--; - - // Everything before the match is the filename - err.filename = linestr.Substring(0, match.Index).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); - //mxd. Get rid of temp directory path - if(err.filename.StartsWith(temppath)) err.filename = err.filename.Replace(temppath, string.Empty); - - if(!Path.IsPathRooted(err.filename)) - { - //mxd. If the error is in an include file, try to find it in loaded resources - if(includes.Contains(err.filename)) - { - foreach(DataReader dr in General.Map.Data.Containers) - { - if(dr is DirectoryReader && dr.FileExists(err.filename)) - { - err.filename = Path.Combine(dr.Location.location, err.filename); - break; - } - } - } - else - { - // Add working directory to filename, so it could be recognized as map namespace lump in MapManager.CompileLump() - err.filename = Path.Combine(processinfo.WorkingDirectory, err.filename); - } - } - - // Everything after the match is the description - err.description = linestr.Substring(match.Index + match.Length).Trim(); - - // Report the error - ReportError(err); - erroradded = true; //mxd - } - - // Next line - line++; - } - - //mxd. Some ACC errors are not properly formatted. If that's the case, threat the whole acs.err as an error... - if(!erroradded && errlines.Length > 0) - { - ReportError(new CompilerError(string.Join(Environment.NewLine, errlines))); - } - } - catch(Exception e) - { - // Error reading errors (ironic, isn't it) - ReportError(new CompilerError("Failed to retrieve compiler error report. " + e.GetType().Name + ": " + e.Message)); - } - } + OnCheckError(includes, processinfo, process, context); return true; } diff --git a/Source/Core/Compilers/BccCompiler.cs b/Source/Core/Compilers/BccCompiler.cs new file mode 100644 index 00000000..75310700 --- /dev/null +++ b/Source/Core/Compilers/BccCompiler.cs @@ -0,0 +1,83 @@ +using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.Data; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; + +namespace CodeImp.DoomBuilder.Compilers +{ + internal class BccCompiler : AccCompiler + { + public BccCompiler(CompilerInfo info) : base(info) {} + + protected override CompileContext OnBeforeProcessStart(ProcessStartInfo info) + { + info.UseShellExecute = false; + info.CreateNoWindow = true; + info.RedirectStandardError = true; + info.RedirectStandardOutput = true; + return new CompileContext(); + } + + protected override void OnCheckError(HashSet includes, ProcessStartInfo processinfo, Process process, CompileContext context) + { + if (process.ExitCode != 0) + { + bool foundAnyErrors = false; + string[] errorLines = process.StandardOutput.ReadToEnd().Split('\n'); + + foreach (string rawErrorLine in errorLines) + { + string[] rawError = rawErrorLine.Split(new char[] { ':' }, 4); + if (rawError.Length != 4) + continue; + string errorFile = rawError[0]; + int errorLine; + if (!int.TryParse(rawError[1], out errorLine)) + continue; + errorLine--; + // rawError[2] is ignored. in BCC, this contains the column at which the error happened. not supported in error viewer. + string errorContent = rawError[3].Trim(); + + // logic copied from AccCompiler + string temppath = this.tempdir.FullName + Path.DirectorySeparatorChar.ToString(); //mxd. Need trailing slash.. + if (errorFile.StartsWith(temppath)) errorFile = errorFile.Replace(temppath, string.Empty); + + if (!Path.IsPathRooted(errorFile)) + { + //mxd. If the error is in an include file, try to find it in loaded resources + if (includes.Contains(errorFile)) + { + foreach (DataReader dr in General.Map.Data.Containers) + { + if (dr is DirectoryReader && dr.FileExists(errorFile)) + { + errorFile = Path.Combine(dr.Location.location, errorFile); + break; + } + } + } + else + { + // Add working directory to filename, so it could be recognized as map namespace lump in MapManager.CompileLump() + errorFile = Path.Combine(processinfo.WorkingDirectory, errorFile); + } + } + // end logic copied from AccCompiler + + CompilerError err = new CompilerError(); + err.linenumber = errorLine; + err.filename = errorFile; + err.description = errorContent; + + ReportError(err); + foundAnyErrors = true; + } + + if (!foundAnyErrors) + ReportError(new CompilerError(string.Join(Environment.NewLine, errorLines))); + } + } + } +}