using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Chernobyl.Collections.Generic.Event; using Chernobyl.ComponentModel; using Chernobyl.Creation; using Chernobyl.Event; using Chernobyl.Plugin.Exceptions; using Microsoft.CSharp; namespace Chernobyl.Plugin { /// /// Creates plug-ins from a set of C# scripts. /// public class CSharpScriptPluginFactory : PluginBuilder, IFactory, Object[]> { /// /// Constructor. /// /// The instance that /// gives and takes services and is given to the created plug-ins /// if they have a constructor that takes an /// instance. public CSharpScriptPluginFactory(IEventCollection services) : this(services, string.Empty) { } /// /// Constructor. /// /// The instance that /// gives and takes services and is given to the created plug-ins /// if they have a constructor that takes an /// instance. /// If this script plug-in factory /// encounters a directory in the file paths passed to it in Create then /// it will use this string to search for the source files within that /// folder. Specify an empty to use the default /// value which is "*.cs". public CSharpScriptPluginFactory(IEventCollection services, string sourceFileSearchPattern) : this(services, sourceFileSearchPattern, SearchOption.AllDirectories) { } /// /// Constructor. /// /// The instance that /// gives and takes services and is given to the created plug-ins /// if they have a constructor that takes an /// instance. /// If this script plug-in factory /// encounters a directory in the file paths passed to it in Create then /// it will use this string to search for the source files within that /// folder. Specify an empty to use the default /// value which is "*.cs". /// If this script plug-in factory /// encounters a directory in the file paths passed to it in Create then /// it will use this to search for the source /// files within that directory/subdirectory. By default, this value is /// . public CSharpScriptPluginFactory(IEventCollection services, string sourceFileSearchPattern, SearchOption sourceFileSearchOption) : this(services, sourceFileSearchPattern, sourceFileSearchOption, null) { } /// /// Constructor. /// /// The instance that /// gives and takes services and is given to the created plug-ins /// if they have a constructor that takes an /// instance. /// If this script plug-in factory /// encounters a directory in the file paths passed to it in Create then /// it will use this string to search for the source files within that /// folder. Specify an empty to use the default /// value which is "*.cs". /// If this script plug-in factory /// encounters a directory in the file paths passed to it in Create then /// it will use this to search for the source /// files within that directory/subdirectory. By default, this value is /// . /// The parameters for the compiler or /// null if the default should be used. public CSharpScriptPluginFactory(IEventCollection services, string sourceFileSearchPattern, SearchOption sourceFileSearchOption, CompilerParameters compilerParameters) : this(services, sourceFileSearchPattern, sourceFileSearchOption, compilerParameters, null) { } /// /// Constructor. /// /// The instance that /// gives and takes services and is given to the created plug-ins /// if they have a constructor that takes an /// instance. /// If this script plug-in factory /// encounters a directory in the file paths passed to it in Create then /// it will use this string to search for the source files within that /// folder. Specify an empty to use the default /// value which is "*.cs". /// If this script plug-in factory /// encounters a directory in the file paths passed to it in Create then /// it will use this to search for the source /// files within that directory/subdirectory. By default, this value is /// . /// The parameters for the compiler or /// null if the default should be used. /// The used /// to compile the C# scripts or null if the default /// should be used. public CSharpScriptPluginFactory(IEventCollection services, string sourceFileSearchPattern, SearchOption sourceFileSearchOption, CompilerParameters compilerParameters, CSharpCodeProvider codeProvider) : base(services) { if (sourceFileSearchPattern.Length == 0) sourceFileSearchPattern = "*.cs"; SourceFileSearchPattern = sourceFileSearchPattern; SourceFileSearchOption = sourceFileSearchOption; if(compilerParameters == null) { compilerParameters = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = true, #if DEBUG IncludeDebugInformation = true, #else IncludeDebugInformation = false, #endif TreatWarningsAsErrors = false }; } CompilerParameters = compilerParameters; if(codeProvider == null) { Dictionary providerOptions = new Dictionary(); providerOptions.Add("CompilerVersion", "v3.5"); codeProvider = new CSharpCodeProvider(providerOptions); } CodeProvider = codeProvider; } /// /// Creates a set of objects, from a set of files, using the /// method to choose which objects to create and /// the method to do the actually creation. /// /// This can be a list of one of the following /// things: /// /// File paths to source files. /// Directory paths that contain source files. /// File paths to source files and directory paths that contain source files. /// Pure C# source code. /// /// The objects that were created. /// Thrown if the compilation of /// the scripts fails. public object[] Create(IEnumerable pathsOrSource) { try { CompilerResults compileResults; object[] objects = Create(out compileResults, pathsOrSource.ToArray()); // Throw an exception for any errors received. if (compileResults.Errors.Count > 0) throw new CompileErrorException("Compilation of some C# " + "scripts has failed.", compileResults); ReportCreation(objects); return objects; } catch (Exception e) { ReportCreation(new CreationEventArgs(e)); throw; } } /// /// If this script plug-in factory encounters a directory in the /// file paths passed to it in Create then it will use this string /// to search for the source files within that folder. By default, /// this value is "*.cs". /// public string SourceFileSearchPattern { get; set; } /// /// If this script plug-in factory encounters a directory in the /// file paths passed to it in Create then it will use this /// to search for the source files within /// that directory/subdirectory. By default, this value is /// . /// public SearchOption SourceFileSearchOption { get; set; } /// /// The parameters for the compiler. /// public CompilerParameters CompilerParameters { get; set; } /// /// The C# code provider used to compile the C# scripts. /// public CSharpCodeProvider CodeProvider { get; set; } /// /// Creates a set of objects, from a set of files, using the /// method to choose which objects to create and /// the method to do the actually creation. /// /// The results from the compilation of the /// C# scripts. /// This can be a list of one of the following /// things: /// /// File paths to source files. /// Directory paths that contain source files. /// File paths to source files and directory paths that contain source files. /// Pure C# source code. /// /// The objects that were created. object[] Create(out CompilerResults compileResults, string[] pathsOrSource) { List interfaces = new List(); if (pathsOrSource.Length == 0) { compileResults = null; return interfaces.ToArray(); } // add in our assemblies Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) CompilerParameters.ReferencedAssemblies.Add(asm.Location); if (File.Exists(pathsOrSource[0]) || Directory.Exists(pathsOrSource[0])) { // this is a set of files or directories // make sure all of the files exist and we get the source files from directories. List sourceFiles = new List(); foreach (string path in pathsOrSource) { // make sure the path is absolute or the compilation will // fail string finalPath = path; if (Path.IsPathRooted(path) == false) finalPath = Path.GetFullPath(path); if (File.Exists(finalPath)) sourceFiles.Add(finalPath); else if (Directory.Exists(finalPath)) sourceFiles.AddRange(Directory.GetFiles(finalPath, SourceFileSearchPattern, SourceFileSearchOption)); } compileResults = CodeProvider.CompileAssemblyFromFile(CompilerParameters, sourceFiles.ToArray()); } else // this is pure source code; compile it. compileResults = CodeProvider.CompileAssemblyFromSource(CompilerParameters, pathsOrSource); // get the interfaces we need from the compile. if (compileResults.Errors.Count == 0) { // get the interfaces from the assembly of compiled scripts foreach (Type type in compileResults.CompiledAssembly.GetTypes()) { if (Predicate(type)) interfaces.Add(Creator(type)); } } return interfaces.ToArray(); } } }