using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using Microsoft.CSharp; using Microsoft.VisualBasic; using rScripting.Compilers; using rScripting.LateBinding; namespace MudEngine.Scripting { /// /// Provides Properties & Methods needed to compile script source code into .NET assemblies. /// public class CompileEngine { /// /// The file extension used for the script files. /// public String ScriptExtension { get { return _ScriptExtension; } set { if (value.StartsWith(".")) _ScriptExtension = value; else _ScriptExtension = "." + value; } } private String _ScriptExtension; /// /// Provides a collection of Assemblies that the compiler will add to its reference list. /// public List AssemblyReferences { get; private set; } /// /// Provides a reference to the assembly generated during script compilation. /// public Assembly CompiledAssembly { get; set; } /// /// The compiler that will be used when the contents of ScriptRepository are compiled. /// public String Compiler { get; set; } /// /// Used to supply compiling options to various compilers if they support this feature. /// public Dictionary CompilerOptions { get; set; } /// /// Used to check if the compilation contained any errors. /// public Boolean HasErrors { get; internal set; } /// /// String of errors that occurred during compilation, if any. /// public String Errors { get { if (_CompileMessages.Length == 0) return "No Errors."; else { StringBuilder builder = new StringBuilder(); foreach (String error in _CompileMessages) { builder.AppendLine(error); } return builder.ToString(); } } } //Messages stored from the compilers CompilerResults property. private String[] _CompileMessages; //Returns all of the assemblies currently loaded in the current domain. private Assembly[] _Assemblies { get { return AppDomain.CurrentDomain.GetAssemblies(); } } public CompileEngine() : this(".cs") { //Passes defaults off to the parameterized constructor. } public CompileEngine(String scriptExtensions) { _CompileMessages = new String[] { "No compiler messages available." }; CompilerOptions = new Dictionary(); Compiler = "C#"; AssemblyReferences = new List(); AssemblyReferences.Add("mscorlib.dll"); AssemblyReferences.Add("System.dll"); AssemblyReferences.Add("System.Core.dll"); ScriptExtension = scriptExtensions; } /// /// Adds a reference to the supplied Assembly name to the compilers reference collection. /// /// public void AddAssemblyReference(String assembly) { if (!AssemblyReferences.Contains(assembly)) AssemblyReferences.Add(assembly); } /// /// Adds a reference to the supplied Assembly to the compilers reference collection. /// /// public void AddAssemblyReference(Assembly assembly) { if (!AssemblyReferences.Contains(assembly.GetName().Name)) AssemblyReferences.Add(assembly.GetName().Name); } /// /// Removes the supplied assembly from the compilers reference collection. /// /// public void RemoveAssemblyReference(String assembly) { if (AssemblyReferences.Contains(assembly)) AssemblyReferences.Remove(assembly); } /// /// Clears the compilers reference collection, leaving it empty. /// public void ClearAssemblyReference() { AssemblyReferences.Clear(); } /// /// Compiles the scripts found within the CompileEngine.ScriptRepository directory that match the CompileEngine.ScriptExtension file extension. /// The compiler will compile the scripts using the compiler specified with the CompileEngine.Compiler Property. /// /// Returns true if compilation was completed without any errors. public Boolean Compile(String scriptRepository) { //Get the compiler that the developer has selected. //If the developer chooses a compiler that is not part of the engine, the GetCompiler() method //will check all the currently loaded assemblies in memory for a custom compiler implementing //the ICompiler interface. Type compiler = GetCompiler(); //Incase a non-default compiler was specified and we could not find it in memory, fail. if (compiler.Name == "ICompiler") { this._CompileMessages = new string[] { "Compilation Failed.", "Unable to locate the specified compiler of Type '" + Compiler + "'." }; return false; } //Get the compiler parameters. CompilerParameters param = GetParameters(); //Create a Instance of the compiler, either custom or internal. ICompiler com = (ICompiler)Activator.CreateInstance(compiler); //Setup it's properties to match that of our CompileEngine. com.AssemblyReferences = AssemblyReferences; com.ScriptExtension = ScriptExtension; com.CompilerOptions = this.CompilerOptions; //Compile the scripts. Boolean isSuccess = com.Compile(param, scriptRepository); HasErrors = !isSuccess; //If the compilation failed, store all of the compiler errors //into our _CompileMessages string. if (!isSuccess) { List compilerMessages = new List(); foreach (String message in com.Results.Output) { compilerMessages.Add(message); } _CompileMessages = compilerMessages.ToArray(); return false; } else { //Compiling completed without error, so we need to save //a reference to the compiled assembly. CompiledAssembly = com.Results.CompiledAssembly; return true; } } /// /// Compiles the script supplied. /// The compiler will compile the script using the compiler specified with the CompileEngine.Compiler Property. /// /// Returns true if compilation was completed without any errors. public Boolean Compile(FileInfo sourceFile) { if (!sourceFile.Exists) { this._CompileMessages = new String[] { "Error: File " + sourceFile.FullName + " does not exists." }; return false; } //Get the compiler that the developer has selected. //If the developer chooses a compiler that is not part of the engine, the GetCompiler() method //will check all the currently loaded assemblies in memory for a custom compiler implementing //the ICompiler interface. Type compiler = GetCompiler(); //Incase a non-default compiler was specified and we could not find it in memory, fail. if (compiler.Name == "ICompiler") { this._CompileMessages = new string[] { "Compilation Failed.", "Unable to locate the specified compiler of Type '" + Compiler + "'." }; return false; } //Get the compiler parameters. CompilerParameters param = GetParameters(); //Create a Instance of the compiler, either custom or internal. ICompiler com = (ICompiler)Activator.CreateInstance(compiler); //Setup it's properties to match that of our CompileEngine. com.AssemblyReferences = AssemblyReferences; com.ScriptExtension = ScriptExtension; com.CompilerOptions = this.CompilerOptions; //Compile the script. Boolean isSuccess = com.Compile(param, sourceFile); HasErrors = !isSuccess; //If the compilation failed, store all of the compiler errors //into our _CompileMessages string. if (!isSuccess) { List compilerMessages = new List(); foreach (String message in com.Results.Output) { compilerMessages.Add(message); } _CompileMessages = compilerMessages.ToArray(); return false; } else { //Compiling completed without error, so we need to save //a reference to the compiled assembly. CompiledAssembly = com.Results.CompiledAssembly; return true; } } /// /// Compiles the source code provided. /// The compiler will compile the scripts using the compiler specified with the CompileEngine.Compiler Property. /// /// Returns true if compilation was completed without any errors. public Boolean Compile(String[] sourceCode) { //Get the compiler that the developer has selected. //If the developer chooses a compiler that is not part of the engine, the GetCompiler() method //will check all the currently loaded assemblies in memory for a custom compiler implementing //the ICompiler interface. Type compiler = GetCompiler(); //Incase a non-default compiler was specified and we could not find it in memory, fail. if (compiler.Name == "ICompiler") { this._CompileMessages = new string[] { "Compilation Failed.", "Unable to locate the specified compiler of Type '" + Compiler + "'." }; return false; } //Get the compiler parameters. CompilerParameters param = GetParameters(); //Create a Instance of the compiler, either custom or internal. ICompiler com = (ICompiler)Activator.CreateInstance(compiler); //Setup it's properties to match that of our CompileEngine. com.AssemblyReferences = AssemblyReferences; com.ScriptExtension = ScriptExtension; com.CompilerOptions = this.CompilerOptions; //Compile the scripts. Boolean isSuccess = com.Compile(param, sourceCode); HasErrors = !isSuccess; //If the compilation failed, store all of the compiler errors //into our _CompileMessages string. if (!isSuccess) { List compilerMessages = new List(); foreach (String message in com.Results.Output) { compilerMessages.Add(message); } _CompileMessages = compilerMessages.ToArray(); return false; } else { //Compiling completed without error, so we need to save //a reference to the compiled assembly. CompiledAssembly = com.Results.CompiledAssembly; return true; } } /// /// Gets compiler parameters that the compiler will be supplied with. /// /// private CompilerParameters GetParameters() { //Setup some default parameters that will be used by the compilers. CompilerParameters param = new CompilerParameters(this.AssemblyReferences.ToArray()); param.GenerateExecutable = false; param.GenerateInMemory = true; //Left out, Add as CompileEngine properties in the future. //param.TreatWarningsAsErrors = true; //param.WarningLevel = 0; //param.IncludeDebugInformation = true; return param; } /// /// Gets the compiler that will be used during the compilation of the scripts. /// If a custom compiler is used, then the method will check every assembly in memory /// and find the custom one requested. If none are found, then it will return a new /// Object of type ICompiler. /// /// private Type GetCompiler() { Type compiler = typeof(ICompiler); //Internal CSharpRaw compiler Type specified, so we'll use that. if ((this.Compiler.ToLower() == "MudCompiler")) { compiler = typeof(MudEngine.Scripting.MudScriptCompiler); return compiler; } //Build a collection of available compilers by scanning all the assemblies loaded in memory. //If any of the assemblies contain a Type that uses the ICompiler interface, we will assume that the //assembly is a add-on assembly for rScript, adding a new compiler to the CompileEngine. //Only used if a non-internal compiler is specified else { //Non-internal compiler supplied, so loop through every assembly loaded in memory foreach (Assembly a in _Assemblies) { Boolean isCompiler = false; //Create an array of all Types within this assembly Type[] types = a.GetTypes(); //Itterate through each Type; See if any implement the ICompiler interface. foreach (Type t in a.GetTypes()) { //If this Type implements ICompiler, then our compiler field needs to reference the Type. if ((t.GetInterface("ICompiler") != null) && (t.Name.ToLower() == Compiler.ToLower())) { //compiler needs to reference this custom compiler Type. compiler = t; isCompiler = true; break; } } //If we found a matching compiler, then exit this loop. if (isCompiler) break; } } return compiler; } } }