Files
abysmal-isle/Scripts/SubSystem/VitaNex/Core/Build/CSharpCompiler.cs
Unstable Kitsune b918192e4e Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
2023-11-28 23:20:26 -05:00

361 lines
8.4 KiB
C#

#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CSharp;
using Server;
using VitaNex.IO;
#endregion
#if !MUO
namespace VitaNex.Build
{
public enum CompileStatus
{
Initializing,
Compiling,
Completed,
Aborted
}
public class CSharpCompiler
{
public static readonly string[] DefaultReferences =
{
"System.dll", "System.Core.dll", "System.Data.dll", "System.Drawing.dll", "System.Web.dll",
"System.Windows.Forms.dll", "System.Xml.dll"
};
private static volatile string _DefaultInputPath = IOUtility.GetSafeDirectoryPath(Core.BaseDirectory + "/Scripts");
private static volatile string _DefaultOutputPath =
IOUtility.GetSafeDirectoryPath(Core.BaseDirectory + "/Scripts/Output");
public static string DefaultInputPath
{
get => _DefaultInputPath;
set => _DefaultInputPath = IOUtility.GetSafeDirectoryPath(value);
}
public static string DefaultOutputPath
{
get => _DefaultOutputPath;
set => _DefaultOutputPath = IOUtility.GetSafeDirectoryPath(value);
}
public bool Debug { get; set; }
public List<string> References { get; private set; }
public List<string> FileMasks { get; private set; }
public List<string> Arguments { get; private set; }
public string[] Errors { get; private set; }
public CompileStatus Status { get; private set; }
public string StatusMessage { get; private set; }
public CSharpCodeProvider Provider { get; private set; }
public CompilerParameters Parameters { get; private set; }
public CompilerResults Results { get; private set; }
public DirectoryInfo InputDirectory { get; set; }
public DirectoryInfo OutputDirectory { get; set; }
public string OutputFileName { get; set; }
public Action<CompilerResults> CompiledCallback { get; set; }
public CSharpCompiler(
DirectoryInfo input,
DirectoryInfo output,
string outputFileName,
Action<CompilerResults> onCompiled = null)
{
References = new List<string>();
FileMasks = new List<string>();
Arguments = new List<string>();
Errors = new string[0];
Status = CompileStatus.Initializing;
StatusMessage = String.Empty;
Provider = new CSharpCodeProvider();
InputDirectory = input;
OutputDirectory = output.EnsureDirectory(false);
OutputFileName = outputFileName;
CompiledCallback = onCompiled;
}
public CSharpCompiler(
string inputPath,
string outputPath,
string outputFileName,
Action<CompilerResults> onCompiled = null)
: this(
new DirectoryInfo(IOUtility.GetSafeDirectoryPath(inputPath)),
IOUtility.EnsureDirectory(outputPath, true),
outputFileName,
onCompiled)
{ }
public CSharpCompiler(string outputFileName, Action<CompilerResults> onCompiled = null)
: this(new DirectoryInfo(_DefaultInputPath), new DirectoryInfo(_DefaultOutputPath), outputFileName, onCompiled)
{ }
public void AddReference(string name)
{
if (!References.Contains(name))
{
References.Add(name);
}
}
public void AddFileMask(string mask)
{
if (!FileMasks.Contains(mask))
{
FileMasks.Add(mask);
}
}
public void Compile(bool async = false)
{
if (Status == CompileStatus.Compiling)
{
return;
}
var pct = new Thread(Init)
{
Name = "VNCSharpCompiler"
};
pct.Start();
if (async)
{
return;
}
VitaNexCore.WaitWhile(
() => Status == CompileStatus.Initializing || Status == CompileStatus.Compiling,
TimeSpan.FromMinutes(5.0));
pct.Join();
}
[STAThread]
private void Init()
{
VitaNexCore.TryCatch(
() =>
{
Status = CompileStatus.Initializing;
if (!InputDirectory.Exists)
{
Status = CompileStatus.Aborted;
StatusMessage = "Input directory '" + InputDirectory + "' does not exist.";
return;
}
var infos = new List<FileInfo>();
foreach (var file in FileMasks.SelectMany(
t => InputDirectory.GetFiles(t, SearchOption.AllDirectories).Where(file => !infos.Contains(file))))
{
infos.Add(file);
}
var files = infos.ToArray();
infos.Clear();
if (files.Length == 0)
{
Status = CompileStatus.Aborted;
StatusMessage = "No files to compile.";
return;
}
var refs = new List<string>();
var fileNames = new List<string>();
foreach (var fName in files.Select(t => t.FullName)
.Where(fName => !String.IsNullOrEmpty(fName))
.Where(fName => !fileNames.Contains(fName)))
{
fileNames.Add(fName);
}
foreach (var t in DefaultReferences.Where(t => !String.IsNullOrEmpty(t)).Where(t => !refs.Contains(t)))
{
refs.Add(t);
}
foreach (var t in References.Where(t => !String.IsNullOrEmpty(t)).Where(t => !refs.Contains(t)))
{
refs.Add(t);
}
var configs = GetConfigFiles();
if (configs != null)
{
foreach (var t in configs.Select(GetConfigAssemblies)
.SelectMany(
asm => asm.Where(t => !String.IsNullOrEmpty(t))
.Where(t => File.Exists(IOUtility.GetSafeFilePath(IOUtility.GetBaseDirectory() + "/" + t, true)))
.Where(t => !refs.Contains(t))))
{
refs.Add(t);
}
}
Status = CompileStatus.Compiling;
Parameters = new CompilerParameters(
refs.ToArray(),
IOUtility.GetUnusedFilePath(OutputDirectory.FullName, OutputFileName),
Debug)
{
GenerateExecutable = false,
WarningLevel = 4,
CompilerOptions = String.Empty
};
foreach (var arg in Arguments)
{
Parameters.CompilerOptions += arg + " ";
}
Results = Provider.CompileAssemblyFromFile(Parameters, fileNames.ToArray());
if (Results.Errors.Count > 0)
{
int errorCount = 0, warningCount = 0;
foreach (CompilerError e in Results.Errors)
{
if (e.IsWarning)
{
++warningCount;
}
else
{
++errorCount;
}
}
Errors = new string[Results.Errors.Count];
for (var e = 0; e < Results.Errors.Count; e++)
{
Errors[e] = String.Format(
"[{0}][{1}][{2}]: Line {3}, Column {4}\n{5}",
Results.Errors[e].IsWarning ? "Warning" : "Error",
Results.Errors[e].FileName,
Results.Errors[e].ErrorNumber,
Results.Errors[e].Line,
Results.Errors[e].Column,
Results.Errors[e].ErrorText);
}
StatusMessage = String.Format(
"Finished compiling with {0} error{1} and {2} warning{3}",
errorCount,
errorCount > 1 ? "s" : "",
warningCount,
warningCount > 1 ? "s" : "");
Status = CompileStatus.Completed;
}
else
{
StatusMessage = "Finished compiling with no errors or warnings.";
Status = CompileStatus.Completed;
}
},
ex =>
{
Status = CompileStatus.Aborted;
StatusMessage = ex.Message;
});
if (CompiledCallback != null)
{
CompiledCallback(Results);
}
}
public FileInfo[] GetConfigFiles()
{
var configs = InputDirectory.GetFiles("*.cfg", SearchOption.AllDirectories);
if (configs.Length > 0)
{
return configs;
}
return null;
}
private string[] GetConfigAssemblies(FileInfo file)
{
var list = new List<string>();
if (file.Exists)
{
var lines = File.ReadAllLines(file.FullName, Encoding.Default);
var content = String.Empty;
var inTag = false;
for (var i = 0; i < lines.Length; i++)
{
if (lines[i].StartsWith("[VNC]"))
{
inTag = true;
lines[i] = lines[i].Replace("[VNC]", String.Empty);
}
else if (lines[i].StartsWith("["))
{
inTag = false;
lines[i] = String.Empty;
}
if (inTag)
{
if (!String.IsNullOrEmpty(lines[i]))
{
content += lines[i].Trim();
}
}
}
var split = content.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
list.AddRange(split.Where(assembly => !String.IsNullOrEmpty(assembly) && !assembly.StartsWith("#")));
}
return list.ToArray();
}
}
}
#endif