/// <summary> /// Compile code /// </summary> /// <param name="outputtype">Type of the assembly to produce</param> /// <param name="code">C# code</param> /// <param name="moduleName">Name of the generated module</param> /// <param name="options">Compilation options</param> /// <returns></returns> public Assembly Compile(CompiledOutputType outputtype, string code, string moduleName, CompileOptions options) { string codeOutputDirectory = null; if (options != null && !string.IsNullOrEmpty(options.CodeOutputDirectory)) { Directory.CreateDirectory(options.CodeOutputDirectory); codeOutputDirectory = Path.GetFullPath(options.CodeOutputDirectory); } // Check that we have .NET if (MaxAvailableNETVersion < _neededVersion) throw new ParsingException(string.Format( ".NET v{0}.{1} is required to compile this script. .NET v{2}.{3} is installed.", _neededVersion.Major, _neededVersion.Minor, MaxAvailableNETVersion.Major, MaxAvailableNETVersion.Minor)); // .NET 3.0 missing method fix bool prov35 = (MaxAvailableNETVersion >= new Version(3, 5)); // Proceed with the gory details CompilerParameters param = new CompilerParameters(); if (outputtype == CompiledOutputType.InMemoryAssembly ) { param.GenerateExecutable = false; // If codeoutput directory is set, generate DLLs with debug info for <code> debuggin param.GenerateInMemory = false; param.IncludeDebugInformation = (codeOutputDirectory != null); } else { param.GenerateExecutable = (outputtype == CompiledOutputType.ConsoleExe || outputtype == CompiledOutputType.WindowsExe); param.GenerateInMemory = false; param.IncludeDebugInformation = (codeOutputDirectory != null); param.OutputAssembly = moduleName; var dir = Path.GetDirectoryName(moduleName); if (!string.IsNullOrEmpty(dir) && (codeOutputDirectory != null) && !Directory.Exists(dir)) Directory.CreateDirectory(dir); } param.WarningLevel = 0; switch (outputtype) { case CompiledOutputType.Library: param.CompilerOptions += " /target:library"; break; case CompiledOutputType.ConsoleExe: param.CompilerOptions += " /target:exe"; break; case CompiledOutputType.WindowsExe: param.CompilerOptions += " /target:winexe"; break; } CompilerResults cr; List<string> toDelete = new List<string>(); Dictionary<string,bool> embedded=new Dictionary<string, bool>(); try { if (options!=null && options.Manifest!=null) { // Only C# 3.5 can do manifests & icons separately. If compiled with .NET 2.0 there will be no file version if (prov35) { string wm = getTempFilename(codeOutputDirectory, str_manifest); toDelete.Add(wm); File.WriteAllBytes(wm, options.Manifest); string wi = getTempFilename(codeOutputDirectory, str_icon); toDelete.Add(wi); File.WriteAllBytes(wi, options.Icon); param.CompilerOptions += " " + Utils.QuoteArg("/win32manifest:" + wm) + " " + Utils.QuoteArg("/win32icon:" + wi); } else { string w32 = getTempFilename(codeOutputDirectory, str_w32resources); toDelete.Add(w32); File.WriteAllBytes(w32, options.Compiled); param.Win32Resource = w32; } } if (options!=null && options.EntryPoint != null) param.MainClass = options.EntryPoint; bool allStrong = true; Dictionary<string,string> resources=new Dictionary<string, string>(); foreach (Ref r in _references) { string location = r.From; string dllName = string.Empty; if (!string.IsNullOrEmpty(r.Name)) { dllName = new AssemblyName(r.Name).Name + ".dll"; } if (string.IsNullOrEmpty(location) && options != null && options.StreamProvider != null) { using (var v = options.StreamProvider(dllName)) if (v != null) { location = getTempFilename(codeOutputDirectory, str_references+Path.GetFileName(dllName)); toDelete.Add(location); using (FileStream fs = new FileStream(location, FileMode.Create, FileAccess.Write, FileShare.Read)) v.WriteTo(fs); } } if (string.IsNullOrEmpty(location)) { location = r.ForceLoad(_verboseWriter).Location; } if (string.IsNullOrEmpty(location)) throw new FileNotFoundException(string.Format("Referenced assembly {0} could not be found", r)); param.ReferencedAssemblies.Add(location); if (options != null && moduleName != null && r.Embed && (CompiledOutputType.WindowsExe == outputtype || CompiledOutputType.ConsoleExe == outputtype) && options.StreamProvider != null) { MemoryStream v= options.StreamProvider(dllName + ".gz"); if (!string.IsNullOrEmpty(r.Name)) { var n = new AssemblyName(r.Name); if (n.GetPublicKeyToken() == null || n.GetPublicKeyToken().Length == 0) { _verboseWriter.WriteVerbose("Compile> Embedded assembly " + r.Name + " does not have a strong name."); allStrong = false; } } if (!embedded.ContainsKey(dllName)) { string prefix = "XSharper.Embedded.Assemblies."; // Let's be nice here, and compress the dll a bit string complocation = createResource(resources, v, location, prefix + dllName, codeOutputDirectory, str_resources + prefix.Replace(".", "\\")); if (complocation != null) toDelete.Add(complocation); embedded[dllName] = true; } } } // If user wanted any files, add them too if (options != null && options.FilesToEmbed != null) { foreach (var pair in options.FilesToEmbed) { string name = (pair.IsAssembly) ? "XSharper.Embedded.Assemblies." : "XSharper.Embedded.Files."; if (pair.IsAssembly) { _verboseWriter.WriteVerbose("Compile> Getting assembly name of " + pair.Location); var pkt = AssemblyName.GetAssemblyName(pair.Location).GetPublicKeyToken(); if (pkt == null || pkt.Length == 0) { _verboseWriter.WriteVerbose("Compile> Embedded assembly " + pair.Location + " does not have a strong name."); allStrong = false; } } string complocation = createResource(resources, null, pair.Location, name + new FileInfo(pair.StreamName).Name, codeOutputDirectory, str_resources + name.Replace(".", "\\")); if (complocation != null) toDelete.Add(complocation); } if (allStrong) { string location = getTempFilename(codeOutputDirectory, str_resources+"XSharper\\Embedded\\Assemblies\\AllStrongName.flag"); toDelete.Add(location); using (FileStream fs = new FileStream(location, FileMode.Create, FileAccess.Write, FileShare.Read)) fs.WriteByte((byte) '1'); } } if (codeOutputDirectory != null) param.TempFiles = new TempFileCollection(codeOutputDirectory, true); foreach (var resource in resources) param.CompilerOptions += " \"/res:" + resource.Key + "," + resource.Value + "\" "; if (options!=null && options.ExtraOptions != null) param.CompilerOptions += " " + options.ExtraOptions; CSharpCodeProvider prov; if (!prov35) prov=new CSharpCodeProvider(); else { Dictionary<string, string> providerOptions = new Dictionary<string, string>(); if (Environment.Version.Major>=4) providerOptions.Add("CompilerVersion", string.Format("v{0}", "4.0")); else providerOptions.Add("CompilerVersion", string.Format("v{0}", "3.5")); // Must do it this way, to prevent loading errors on machines with .net 2.0 prov = (CSharpCodeProvider) Activator.CreateInstance(typeof (CSharpCodeProvider), new object[] {providerOptions}); } _verboseWriter.WriteVerbose("Compile> " + Dump.ToDump(param)); cr = prov.CompileAssemblyFromSource(param, code); // Do some beautification if (outputtype != CompiledOutputType.InMemoryAssembly && codeOutputDirectory != null) beautifyOutput(prov35, outputtype, options, param, resources, moduleName, _neededVersion); _verboseWriter.WriteVerbose("Compile> -- Completed --"); // Fire compilation if (cr.Errors != null && cr.Errors.Count != 0) { if (File.Exists(cr.PathToAssembly)) File.Delete(cr.PathToAssembly); StringBuilder sb= new StringBuilder(); sb.Append("C# code compilation error:\n"); string[] lines = code.Split('\n'); foreach (CompilerError error in cr.Errors) { if (error != null) { string line = (error.Line >= 1 && error.Line <= lines.Length) ? lines[error.Line - 1] : "???"; sb.AppendLine(string.Format("Line: {0}\nError: {1}", line.TrimEnd(), error.ErrorText)); } } _verboseWriter.WriteVerbose("Compile> Errors: " + sb.ToString()); throw new ParsingException(sb.ToString()); } if (outputtype == CompiledOutputType.InMemoryAssembly) { byte[] asmData = File.ReadAllBytes(cr.PathToAssembly); File.Delete(cr.PathToAssembly); Assembly a=Assembly.Load(asmData); return a; } } finally { if (string.IsNullOrEmpty(codeOutputDirectory) || outputtype == CompiledOutputType.InMemoryAssembly) foreach (string na in toDelete) File.Delete(na); } return outputtype == CompiledOutputType.InMemoryAssembly ? cr.CompiledAssembly : null; }
/// <summary> /// Compile code /// </summary> /// <param name="outputtype">Type of the assembly to produce</param> /// <param name="code">C# code</param> /// <param name="moduleName">Name of the generated module</param> /// <param name="options">Compilation options</param> /// <returns></returns> public Assembly Compile(CompiledOutputType outputtype, string code, string moduleName, CompileOptions options) { string codeOutputDirectory = null; if (options != null && !string.IsNullOrEmpty(options.CodeOutputDirectory)) { Directory.CreateDirectory(options.CodeOutputDirectory); codeOutputDirectory = Path.GetFullPath(options.CodeOutputDirectory); } // Check that we have .NET if (MaxAvailableNETVersion < _neededVersion) { throw new ParsingException(string.Format( ".NET v{0}.{1} is required to compile this script. .NET v{2}.{3} is installed.", _neededVersion.Major, _neededVersion.Minor, MaxAvailableNETVersion.Major, MaxAvailableNETVersion.Minor)); } // .NET 3.0 missing method fix bool prov35 = (MaxAvailableNETVersion >= new Version(3, 5)); // Proceed with the gory details CompilerParameters param = new CompilerParameters(); if (outputtype == CompiledOutputType.InMemoryAssembly) { param.GenerateExecutable = false; // If codeoutput directory is set, generate DLLs with debug info for <code> debuggin param.GenerateInMemory = false; param.IncludeDebugInformation = (codeOutputDirectory != null); } else { param.GenerateExecutable = (outputtype == CompiledOutputType.ConsoleExe || outputtype == CompiledOutputType.WindowsExe); param.GenerateInMemory = false; param.IncludeDebugInformation = (codeOutputDirectory != null); param.OutputAssembly = moduleName; var dir = Path.GetDirectoryName(moduleName); if (!string.IsNullOrEmpty(dir) && (codeOutputDirectory != null) && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } } param.WarningLevel = 0; switch (outputtype) { case CompiledOutputType.Library: param.CompilerOptions += " /target:library"; break; case CompiledOutputType.ConsoleExe: param.CompilerOptions += " /target:exe"; break; case CompiledOutputType.WindowsExe: param.CompilerOptions += " /target:winexe"; break; } CompilerResults cr; List <string> toDelete = new List <string>(); Dictionary <string, bool> embedded = new Dictionary <string, bool>(); try { if (options != null && options.Manifest != null) { // Only C# 3.5 can do manifests & icons separately. If compiled with .NET 2.0 there will be no file version if (prov35) { string wm = getTempFilename(codeOutputDirectory, str_manifest); toDelete.Add(wm); File.WriteAllBytes(wm, options.Manifest); string wi = getTempFilename(codeOutputDirectory, str_icon); toDelete.Add(wi); File.WriteAllBytes(wi, options.Icon); param.CompilerOptions += " " + Utils.QuoteArg("/win32manifest:" + wm) + " " + Utils.QuoteArg("/win32icon:" + wi); } else { string w32 = getTempFilename(codeOutputDirectory, str_w32resources); toDelete.Add(w32); File.WriteAllBytes(w32, options.Compiled); param.Win32Resource = w32; } } if (options != null && options.EntryPoint != null) { param.MainClass = options.EntryPoint; } bool allStrong = true; Dictionary <string, string> resources = new Dictionary <string, string>(); foreach (Ref r in _references) { string location = r.From; string dllName = string.Empty; if (!string.IsNullOrEmpty(r.Name)) { dllName = new AssemblyName(r.Name).Name + ".dll"; } if (string.IsNullOrEmpty(location) && options != null && options.StreamProvider != null) { using (var v = options.StreamProvider(dllName)) if (v != null) { location = getTempFilename(codeOutputDirectory, str_references + Path.GetFileName(dllName)); toDelete.Add(location); using (FileStream fs = new FileStream(location, FileMode.Create, FileAccess.Write, FileShare.Read)) v.WriteTo(fs); } } if (string.IsNullOrEmpty(location)) { location = r.ForceLoad(_verboseWriter).Location; } if (string.IsNullOrEmpty(location)) { throw new FileNotFoundException(string.Format("Referenced assembly {0} could not be found", r)); } param.ReferencedAssemblies.Add(location); if (options != null && moduleName != null && r.Embed && (CompiledOutputType.WindowsExe == outputtype || CompiledOutputType.ConsoleExe == outputtype) && options.StreamProvider != null) { MemoryStream v = options.StreamProvider(dllName + ".gz"); if (!string.IsNullOrEmpty(r.Name)) { var n = new AssemblyName(r.Name); if (n.GetPublicKeyToken() == null || n.GetPublicKeyToken().Length == 0) { _verboseWriter.WriteVerbose("Compile> Embedded assembly " + r.Name + " does not have a strong name."); allStrong = false; } } if (!embedded.ContainsKey(dllName)) { string prefix = "XSharper.Embedded.Assemblies."; // Let's be nice here, and compress the dll a bit string complocation = createResource(resources, v, location, prefix + dllName, codeOutputDirectory, str_resources + prefix.Replace(".", "\\")); if (complocation != null) { toDelete.Add(complocation); } embedded[dllName] = true; } } } // If user wanted any files, add them too if (options != null && options.FilesToEmbed != null) { foreach (var pair in options.FilesToEmbed) { string name = (pair.IsAssembly) ? "XSharper.Embedded.Assemblies." : "XSharper.Embedded.Files."; if (pair.IsAssembly) { _verboseWriter.WriteVerbose("Compile> Getting assembly name of " + pair.Location); var pkt = AssemblyName.GetAssemblyName(pair.Location).GetPublicKeyToken(); if (pkt == null || pkt.Length == 0) { _verboseWriter.WriteVerbose("Compile> Embedded assembly " + pair.Location + " does not have a strong name."); allStrong = false; } } string complocation = createResource(resources, null, pair.Location, name + new FileInfo(pair.StreamName).Name, codeOutputDirectory, str_resources + name.Replace(".", "\\")); if (complocation != null) { toDelete.Add(complocation); } } if (allStrong) { string location = getTempFilename(codeOutputDirectory, str_resources + "XSharper\\Embedded\\Assemblies\\AllStrongName.flag"); toDelete.Add(location); using (FileStream fs = new FileStream(location, FileMode.Create, FileAccess.Write, FileShare.Read)) fs.WriteByte((byte)'1'); } } if (codeOutputDirectory != null) { param.TempFiles = new TempFileCollection(codeOutputDirectory, true); } foreach (var resource in resources) { param.CompilerOptions += " \"/res:" + resource.Key + "," + resource.Value + "\" "; } if (options != null && options.ExtraOptions != null) { param.CompilerOptions += " " + options.ExtraOptions; } CSharpCodeProvider prov; if (!prov35) { prov = new CSharpCodeProvider(); } else { Dictionary <string, string> providerOptions = new Dictionary <string, string>(); if (Environment.Version.Major >= 4) { providerOptions.Add("CompilerVersion", string.Format("v{0}", "4.0")); } else { providerOptions.Add("CompilerVersion", string.Format("v{0}", "3.5")); } // Must do it this way, to prevent loading errors on machines with .net 2.0 prov = (CSharpCodeProvider)Activator.CreateInstance(typeof(CSharpCodeProvider), new object[] { providerOptions }); } _verboseWriter.WriteVerbose("Compile> " + Dump.ToDump(param)); cr = prov.CompileAssemblyFromSource(param, code); // Do some beautification if (outputtype != CompiledOutputType.InMemoryAssembly && codeOutputDirectory != null) { beautifyOutput(prov35, outputtype, options, param, resources, moduleName, _neededVersion); } _verboseWriter.WriteVerbose("Compile> -- Completed --"); // Fire compilation if (cr.Errors != null && cr.Errors.Count != 0) { if (File.Exists(cr.PathToAssembly)) { File.Delete(cr.PathToAssembly); } StringBuilder sb = new StringBuilder(); sb.Append("C# code compilation error:\n"); string[] lines = code.Split('\n'); foreach (CompilerError error in cr.Errors) { if (error != null) { string line = (error.Line >= 1 && error.Line <= lines.Length) ? lines[error.Line - 1] : "???"; sb.AppendLine(string.Format("Line: {0}\nError: {1}", line.TrimEnd(), error.ErrorText)); } } _verboseWriter.WriteVerbose("Compile> Errors: " + sb.ToString()); throw new ParsingException(sb.ToString()); } if (outputtype == CompiledOutputType.InMemoryAssembly) { byte[] asmData = File.ReadAllBytes(cr.PathToAssembly); File.Delete(cr.PathToAssembly); Assembly a = Assembly.Load(asmData); return(a); } } finally { if (string.IsNullOrEmpty(codeOutputDirectory) || outputtype == CompiledOutputType.InMemoryAssembly) { foreach (string na in toDelete) { File.Delete(na); } } } return(outputtype == CompiledOutputType.InMemoryAssembly ? cr.CompiledAssembly : null); }