Fixed BCS compiler error reporting

This commit is contained in:
ZZYZX 2024-01-28 02:16:42 +02:00
parent 14a494078a
commit 4e34897ac1
5 changed files with 192 additions and 89 deletions

View file

@ -7,7 +7,7 @@ compilers
// All others are the required files (the setting names do not matter) // All others are the required files (the setting names do not matter)
bcc bcc
{ {
interface = "AccCompiler"; interface = "BccCompiler";
program = "bcc.exe"; program = "bcc.exe";
zcommon = "zcommon.bcs"; zcommon = "zcommon.bcs";
std = "std.acs"; std = "std.acs";

View file

@ -1,5 +1,5 @@
URL https://ultimatedoombuilder.github.io/files/ URL https://ultimatedoombuilder.github.io/files/
FileName Builder.exe FileName Builder.exe
UpdateName UltimateDoomBuilder-r[REVNUM]-x86.7z UpdateName UltimateDoomBuilder-r[REVNUM]-x64.7z
InstallerName UltimateDoomBuilder-Setup-R[REVNUM]-x86.exe InstallerName UltimateDoomBuilder-Setup-R[REVNUM]-x64.exe
UpdaterName UDB_Updater-x86.7z UpdaterName UDB_Updater-x64.7z

View file

@ -141,6 +141,7 @@
--> -->
<ItemGroup> <ItemGroup>
<Compile Include="Compilers\AccCompiler.cs" /> <Compile Include="Compilers\AccCompiler.cs" />
<Compile Include="Compilers\BccCompiler.cs" />
<Compile Include="Compilers\NodesCompiler.cs" /> <Compile Include="Compilers\NodesCompiler.cs" />
<Compile Include="Config\ArgumentInfo.cs" /> <Compile Include="Config\ArgumentInfo.cs" />
<Compile Include="Config\ExternalCommandSettings.cs" /> <Compile Include="Config\ExternalCommandSettings.cs" />

View file

@ -30,8 +30,14 @@ using CodeImp.DoomBuilder.ZDoom.Scripting;
namespace CodeImp.DoomBuilder.Compilers namespace CodeImp.DoomBuilder.Compilers
{ {
internal sealed class AccCompiler : Compiler internal class AccCompiler : Compiler
{ {
#region ================== Internal classes
protected class CompileContext { }
#endregion
#region ================== Constants #region ================== Constants
private const string ACS_ERROR_FILE = "acs.err"; private const string ACS_ERROR_FILE = "acs.err";
@ -75,11 +81,101 @@ namespace CodeImp.DoomBuilder.Compilers
#region ================== Methods #region ================== Methods
protected virtual CompileContext OnBeforeProcessStart(ProcessStartInfo info)
{
return new CompileContext();
}
protected virtual void OnCheckError(HashSet<string> 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 // This runs the compiler
public override bool Run() public override bool Run()
{ {
Process process; Process process;
int line = 0;
string sourcedir = Path.GetDirectoryName(sourcefile); string sourcedir = Path.GetDirectoryName(sourcefile);
// Preprocess the file // Preprocess the file
@ -175,6 +271,8 @@ namespace CodeImp.DoomBuilder.Compilers
processinfo.WindowStyle = ProcessWindowStyle.Hidden; processinfo.WindowStyle = ProcessWindowStyle.Hidden;
processinfo.WorkingDirectory = this.workingdir; processinfo.WorkingDirectory = this.workingdir;
CompileContext context = OnBeforeProcessStart(processinfo);
// Output info // Output info
General.WriteLogLine("Running compiler..."); General.WriteLogLine("Running compiler...");
General.WriteLogLine("Program: " + processinfo.FileName); General.WriteLogLine("Program: " + processinfo.FileName);
@ -198,86 +296,7 @@ namespace CodeImp.DoomBuilder.Compilers
General.WriteLogLine("Compiler process has finished."); General.WriteLogLine("Compiler process has finished.");
General.WriteLogLine("Compile time: " + deltatime.TotalSeconds.ToString("########0.00") + " seconds"); General.WriteLogLine("Compile time: " + deltatime.TotalSeconds.ToString("########0.00") + " seconds");
// Now find the error file OnCheckError(includes, processinfo, process, context);
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));
}
}
return true; return true;
} }

View file

@ -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<string> 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)));
}
}
}
}