working on generalized compiler interfacing

This commit is contained in:
codeimp 2008-11-14 16:19:25 +00:00
parent fdb004d7c3
commit 0fa7409d96
11 changed files with 302 additions and 73 deletions

View file

@ -20,12 +20,11 @@ With this interface supports the following placeholders in command-line paramete
%FO indicates the output path and filename.
%PI indicates the path of the input file (without filename).
%PO indicates the path of the output file (without filename).
%PT indicates the temporary directory path where the compiler is located.
%PW indicates the path of the open wad file when compiled as internal script lump.
If compiled as file, or the wad file is not saved, %PW is the same as %PT
These placeholders are case-sensitive!
-------------------------------------------------------------------------------------

View file

@ -26,15 +26,19 @@ using System.Diagnostics;
using System.IO;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.IO;
using System.Windows.Forms;
using System.Text.RegularExpressions;
#endregion
namespace CodeImp.DoomBuilder.Compilers
{
public sealed class AccCompiler : Compiler
internal sealed class AccCompiler : Compiler
{
#region ================== Constants
private const string ACS_ERROR_FILE = "acs.err";
#endregion
#region ================== Variables
@ -65,15 +69,113 @@ namespace CodeImp.DoomBuilder.Compilers
#region ================== Methods
// This runs the compiler with a file as input.
public override bool CompileFile(string filename)
{
return true;
}
// This runs the compiler with lump data as input.
public override bool CompileLump(Stream lumpdata)
// This runs the compiler
public override bool Run()
{
ProcessStartInfo processinfo;
Process process;
TimeSpan deltatime;
string waddir = null;
int line = 0;
// Find wad directory
if(General.Map.FilePathName.Length > 0)
waddir = Path.GetDirectoryName(General.Map.FilePathName);
// When no luck, use temp path
if(waddir == null) waddir = this.tempdir.FullName;
// Create parameters
string args = this.parameters;
args = args.Replace("%FI", inputfile);
args = args.Replace("%FO", outputfile);
args = args.Replace("%PT", this.tempdir.FullName);
args = args.Replace("%PW", waddir);
// Setup process info
processinfo = new ProcessStartInfo();
processinfo.Arguments = args;
processinfo.FileName = Path.Combine(this.tempdir.FullName, info.ProgramFile);
processinfo.CreateNoWindow = false;
processinfo.ErrorDialog = false;
processinfo.UseShellExecute = true;
processinfo.WindowStyle = ProcessWindowStyle.Hidden;
processinfo.WorkingDirectory = this.workingdir;
// Output info
General.WriteLogLine("Running compiler...");
General.WriteLogLine("Program: " + processinfo.FileName);
General.WriteLogLine("Arguments: " + processinfo.Arguments);
try
{
// Start the compiler
process = Process.Start(processinfo);
}
catch(Exception e)
{
// Unable to start the compiler
General.ShowErrorMessage("Unable to start the compiler (" + info.Name + "). " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK);
return false;
}
// Wait for compiler to complete
process.WaitForExit();
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.tempdir.FullName, ACS_ERROR_FILE);
if(File.Exists(errfile))
{
try
{
// Regex to find error lines
Regex errlinematcher = new Regex("\\:[0-9]+\\:\\b", RegexOptions.Compiled | RegexOptions.CultureInvariant);
// Read all lines
string[] errlines = File.ReadAllLines(errfile);
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;
// Everything before the match is the filename
err.filename = linestr.Substring(0, match.Index);
if(!Path.IsPathRooted(err.filename))
{
// Add working directory to filename
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);
}
// Next line
line++;
}
}
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;
}

View file

@ -40,6 +40,8 @@ namespace CodeImp.DoomBuilder.Compilers
protected CompilerInfo info;
protected string parameters;
protected string workingdir;
protected string outputfile;
protected string inputfile;
// Files
protected DirectoryInfo tempdir;
@ -53,9 +55,11 @@ namespace CodeImp.DoomBuilder.Compilers
#endregion
#region ================== Properties
public string Parameters { get { return parameters; } set { parameters = value; } }
public string WorkingDirectory { get { return workingdir; } set { workingdir = value; } }
public string InputFile { get { return inputfile; } set { inputfile = value; } }
public string OutputFile { get { return outputfile; } set { outputfile = value; } }
public string Location { get { return tempdir.FullName; } }
public bool IsDisposed { get { return isdisposed; } }
public CompilerError[] Errors { get { return errors.ToArray(); } }
@ -114,16 +118,10 @@ namespace CodeImp.DoomBuilder.Compilers
}
/// <summary>
/// This runs the compiler with a file as input.
/// This runs the compiler.
/// </summary>
/// <returns>Returns false when failed to start.</returns>
public virtual bool CompileFile(string filename) { return false; }
/// <summary>
/// This runs the compiler with lump data as input.
/// </summary>
/// <returns>Returns false when failed to start.</returns>
public virtual bool CompileLump(Stream lumpdata) { return false; }
public virtual bool Run() { return false; }
/// <summary>
/// Use this to report an error.
@ -151,8 +149,14 @@ namespace CodeImp.DoomBuilder.Compilers
// Go for all assemblies
foreach(Assembly a in asms)
{
Type[] types;
// Find the class
Type[] types = a.GetExportedTypes();
if(a == General.ThisAssembly)
types = a.GetTypes();
else
types = a.GetExportedTypes();
foreach(Type t in types)
{
if(t.IsSubclassOf(typeof(Compiler)) && (t.Name == info.ProgramInterface))

View file

@ -30,11 +30,30 @@ namespace CodeImp.DoomBuilder.Compilers
{
public struct CompilerError
{
// Constants
public const int NO_LINE_NUMBER = -1;
// Members
public string description;
public string filename;
public int linenumber;
// Constructor
public CompilerError(string description)
{
this.description = description;
this.filename = "";
this.linenumber = NO_LINE_NUMBER;
}
// Constructor
public CompilerError(string description, string filename)
{
this.description = description;
this.filename = filename;
this.linenumber = NO_LINE_NUMBER;
}
// Constructor
public CompilerError(string description, string filename, int linenumber)
{

View file

@ -31,7 +31,7 @@ using System.Windows.Forms;
namespace CodeImp.DoomBuilder.Compilers
{
public sealed class NodesCompiler : Compiler
internal sealed class NodesCompiler : Compiler
{
#region ================== Constants
@ -39,15 +39,10 @@ namespace CodeImp.DoomBuilder.Compilers
#region ================== Variables
// Output file
private string outputfile;
#endregion
#region ================== Properties
public string OutputFile { get { return outputfile; } set { outputfile = value; } }
#endregion
#region ================== Constructor / Disposer
@ -79,7 +74,7 @@ namespace CodeImp.DoomBuilder.Compilers
#region ================== Methods
// This runs the compiler with a file as input.
public override bool CompileFile(string filename)
public override bool Run()
{
ProcessStartInfo processinfo;
Process process;
@ -87,7 +82,7 @@ namespace CodeImp.DoomBuilder.Compilers
// Create parameters
string args = this.parameters;
args = args.Replace("%FI", filename);
args = args.Replace("%FI", inputfile);
args = args.Replace("%FO", outputfile);
// Setup process info
@ -98,7 +93,7 @@ namespace CodeImp.DoomBuilder.Compilers
processinfo.ErrorDialog = false;
processinfo.UseShellExecute = true;
processinfo.WindowStyle = ProcessWindowStyle.Hidden;
processinfo.WorkingDirectory = this.tempdir.FullName;
processinfo.WorkingDirectory = this.workingdir;
// Output info
General.WriteLogLine("Running compiler...");

View file

@ -66,7 +66,7 @@ namespace CodeImp.DoomBuilder.Config
string compilername;
General.WriteLogLine("Registered nodebuilder configuration '" + name + "' from '" + filename + "'");
// Initialize
this.name = name;
this.compiler = null;
@ -111,20 +111,9 @@ namespace CodeImp.DoomBuilder.Config
}
// This runs the nodebuilder
public NodesCompiler CreateCompiler()
public Compiler CreateCompiler()
{
Compiler c = compiler.Create();
if(c is NodesCompiler)
{
NodesCompiler ns = (c as NodesCompiler);
ns.Parameters = parameters;
return ns;
}
else
{
// Nodebuilders must use a NodesCompiler!
throw new ArgumentException("Cannot create compiler interface '" + compiler.ProgramInterface + "' for nodebuilder '" + name + "'. Nodebuilders must use a NodesCompiler compiler interface!");
}
return compiler.Create();
}
#endregion

View file

@ -93,7 +93,12 @@ namespace CodeImp.DoomBuilder.Controls
#endregion
#region ================== Methods
// This compiles the script
public virtual void Compile()
{
}
// This saves the document (used for both explicit and implicit)
// Return true when successfully saved
public virtual bool Save()

View file

@ -394,7 +394,7 @@ namespace CodeImp.DoomBuilder.Controls
return true;
}
}
// A tab is selected
private void tabs_Selecting(object sender, TabControlCancelEventArgs e)
{
@ -408,16 +408,19 @@ namespace CodeImp.DoomBuilder.Controls
CloseScript(t, false);
UpdateToolbar();
}
// Compile Script clicked
private void buttoncompile_Click(object sender, EventArgs e)
{
// First save all implicit scripts to the temporary wad file
ImplicitSave();
// TODO: Now compile this lump
// Compile script
ScriptDocumentTab t = (tabs.SelectedTab as ScriptDocumentTab);
t.Compile();
UpdateToolbar();
}
// Undo clicked
private void buttonundo_Click(object sender, EventArgs e)
{
@ -425,7 +428,7 @@ namespace CodeImp.DoomBuilder.Controls
t.Undo();
UpdateToolbar();
}
// Redo clicked
private void buttonredo_Click(object sender, EventArgs e)
{
@ -433,7 +436,7 @@ namespace CodeImp.DoomBuilder.Controls
t.Redo();
UpdateToolbar();
}
// Cut clicked
private void buttoncut_Click(object sender, EventArgs e)
{
@ -441,7 +444,7 @@ namespace CodeImp.DoomBuilder.Controls
t.Cut();
UpdateToolbar();
}
// Copy clicked
private void buttoncopy_Click(object sender, EventArgs e)
{

View file

@ -30,6 +30,7 @@ using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.IO;
using System.IO;
using CodeImp.DoomBuilder.Compilers;
#endregion
@ -80,6 +81,49 @@ namespace CodeImp.DoomBuilder.Controls
#region ================== Methods
// This compiles the script file
public override void Compile()
{
DirectoryInfo tempdir;
Compiler compiler;
string inputfile, outputfile;
// List of errors
List<CompilerError> errors = new List<CompilerError>();
try
{
// Initialize compiler
compiler = config.Compiler.Create();
}
catch(Exception e)
{
// Fail
errors.Add(new CompilerError("Unable to initialize compiler. " + e.GetType().Name + ": " + e.Message));
return;
}
// Make random output filename
outputfile = General.MakeTempFilename(compiler.Location, "tmp");
// Run compiler
compiler.Parameters = config.Parameters;
compiler.InputFile = Path.GetFileName(filepathname);
compiler.OutputFile = Path.GetFileName(outputfile);
compiler.WorkingDirectory = Path.GetDirectoryName(filepathname);
if(compiler.Run())
{
// Fetch errors
errors.AddRange(compiler.Errors);
}
// Dispose compiler
compiler.Dispose();
// TODO: Feed errors to panel
}
// This saves the document (used for both explicit and implicit)
// Return true when successfully saved
public override bool Save()

View file

@ -40,9 +40,9 @@ namespace CodeImp.DoomBuilder.Controls
#region ================== Constants
#endregion
#region ================== Variables
private string lumpname;
#endregion
@ -90,6 +90,13 @@ namespace CodeImp.DoomBuilder.Controls
#region ================== Methods
// Compile script
public override void Compile()
{
// Do it!
General.Map.CompileLump(lumpname);
}
// Implicit save
public override bool Save()
{

View file

@ -89,6 +89,7 @@ namespace CodeImp.DoomBuilder
private Launcher launcher;
private ThingsFilter thingsfilter;
private ScriptEditorForm scriptwindow;
private List<CompilerError> errors;
// Disposing
private bool isdisposed = false;
@ -121,6 +122,7 @@ namespace CodeImp.DoomBuilder
public IMapSetIO FormatInterface { get { return io; } }
internal Launcher Launcher { get { return launcher; } }
public ThingsFilter ThingsFilter { get { return thingsfilter; } }
internal List<CompilerError> Errors { get { return errors; } }
public bool IsScriptsWindowOpen { get { return (scriptwindow != null) && !scriptwindow.IsDisposed; } }
#endregion
@ -557,7 +559,7 @@ namespace CodeImp.DoomBuilder
{
// Create the compiler interface that will run the nodebuilder
// This automatically creates a temporary directory for us
NodesCompiler compiler = nodebuilder.CreateCompiler();
Compiler compiler = nodebuilder.CreateCompiler();
// Make temporary filename
tempfile1 = General.MakeTempFilename(compiler.Location);
@ -587,8 +589,11 @@ namespace CodeImp.DoomBuilder
buildwad.Dispose();
// Run the nodebuilder
compiler.Parameters = nodebuilder.Parameters;
compiler.InputFile = Path.GetFileName(tempfile1);
compiler.OutputFile = Path.GetFileName(tempfile2);
if(compiler.CompileFile(Path.GetFileName(tempfile1)))
compiler.WorkingDirectory = Path.GetDirectoryName(tempfile1);
if(compiler.Run())
{
// Open the output file
buildwad = new WAD(tempfile2);
@ -1116,27 +1121,84 @@ namespace CodeImp.DoomBuilder
}
// This compiles a script lump and returns any errors that may have occurred
internal CompilerError[] CompileLump(Lump scriptlump, bool showwarning)
// Returns true when our code worked properly (even when the compiler returned errors)
internal bool CompileLump(string lumpname)
{
DirectoryInfo tempdir;
CompilerError[] errors;
Compiler compiler;
string inputfile, outputfile;
// Find the lump
Lump lump = tempwad.FindLump(lumpname);
if(lump == null) throw new Exception("No such lump in temporary wad file '" + lumpname + "'.");
// New list of errors
errors = new List<CompilerError>();
// Determine the script configuration to use
ScriptConfiguration scriptconfig = config.MapLumps[scriptlump.Name].script;
// Initialize compiler
Compiler compiler = scriptconfig.Compiler.Create();
ScriptConfiguration scriptconfig = config.MapLumps[lump.Name].script;
try
{
// Initialize compiler
compiler = scriptconfig.Compiler.Create();
}
catch(Exception e)
{
// Fail
errors.Add(new CompilerError("Unable to initialize compiler. " + e.GetType().Name + ": " + e.Message));
return false;
}
try
{
// Write lump data to temp script file in compiler's temp directory
inputfile = General.MakeTempFilename(compiler.Location, "tmp");
lump.Stream.Seek(0, SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(lump.Stream);
File.WriteAllBytes(inputfile, reader.ReadBytes((int)lump.Stream.Length));
}
catch(Exception e)
{
// Fail
compiler.Dispose();
errors.Add(new CompilerError("Unable to write script to working file. " + e.GetType().Name + ": " + e.Message));
return false;
}
// Make random output filename
outputfile = General.MakeTempFilename(compiler.Location, "tmp");
// Run compiler
compiler.Parameters = scriptconfig.Parameters;
errors = compiler.Errors;
// Clean up
compiler.Dispose();
// Done
return errors;
compiler.InputFile = Path.GetFileName(inputfile);
compiler.OutputFile = Path.GetFileName(outputfile);
compiler.WorkingDirectory = Path.GetDirectoryName(inputfile);
if(compiler.Run())
{
// Fetch errors
errors.AddRange(compiler.Errors);
// Clean up
compiler.Dispose();
if(errors.Count == 0) errors = null;
// Done
return true;
}
else
{
// Fail
compiler.Dispose();
errors = null;
return false;
}
}
// This clears all compiler errors
internal void ClearCompilerErrors()
{
errors = null;
}
#endregion