mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
6.6 KiB
268 lines
6.6 KiB
using System; |
|
using System.CodeDom.Compiler; |
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.IO; |
|
using System.Text; |
|
|
|
using NUnit.Framework; |
|
|
|
namespace Mono.Cecil.Tests { |
|
|
|
struct CompilationResult { |
|
internal DateTime source_write_time; |
|
internal string result_file; |
|
|
|
public CompilationResult (DateTime write_time, string result_file) |
|
{ |
|
this.source_write_time = write_time; |
|
this.result_file = result_file; |
|
} |
|
} |
|
|
|
class Platform { |
|
|
|
public static bool OnMono { get { return typeof (object).Assembly.GetType ("Mono.Runtime") != null; } } |
|
} |
|
|
|
abstract class CompilationService { |
|
|
|
Dictionary<string, CompilationResult> files = new Dictionary<string, CompilationResult> (); |
|
|
|
bool TryGetResult (string name, out string file_result) |
|
{ |
|
file_result = null; |
|
CompilationResult result; |
|
if (!files.TryGetValue (name, out result)) |
|
return false; |
|
|
|
if (result.source_write_time != File.GetLastWriteTime (name)) |
|
return false; |
|
|
|
file_result = result.result_file; |
|
return true; |
|
} |
|
|
|
public string Compile (string name) |
|
{ |
|
string result_file; |
|
if (TryGetResult (name, out result_file)) |
|
return result_file; |
|
|
|
result_file = CompileFile (name); |
|
RegisterFile (name, result_file); |
|
return result_file; |
|
} |
|
|
|
void RegisterFile (string name, string result_file) |
|
{ |
|
files [name] = new CompilationResult (File.GetLastWriteTime (name), result_file); |
|
} |
|
|
|
protected abstract string CompileFile (string name); |
|
|
|
public static string CompileResource (string name) |
|
{ |
|
var extension = Path.GetExtension (name); |
|
if (extension == ".il") |
|
return IlasmCompilationService.Instance.Compile (name); |
|
|
|
if (extension == ".cs" || extension == ".vb") |
|
return CodeDomCompilationService.Instance.Compile (name); |
|
|
|
throw new NotSupportedException (extension); |
|
} |
|
|
|
protected static string GetCompiledFilePath (string file_name) |
|
{ |
|
var tmp_cecil = Path.Combine (Path.GetTempPath (), "cecil"); |
|
if (!Directory.Exists (tmp_cecil)) |
|
Directory.CreateDirectory (tmp_cecil); |
|
|
|
return Path.Combine (tmp_cecil, Path.GetFileName (file_name) + ".dll"); |
|
} |
|
|
|
public static void Verify (string name) |
|
{ |
|
var output = Platform.OnMono ? ShellService.PEDump (name) : ShellService.PEVerify (name); |
|
if (output.ExitCode != 0) |
|
Assert.Fail (output.ToString ()); |
|
} |
|
} |
|
|
|
class IlasmCompilationService : CompilationService { |
|
|
|
public static readonly IlasmCompilationService Instance = new IlasmCompilationService (); |
|
|
|
protected override string CompileFile (string name) |
|
{ |
|
string file = GetCompiledFilePath (name); |
|
|
|
var output = ShellService.ILAsm (name, file); |
|
|
|
AssertAssemblerResult (output); |
|
|
|
return file; |
|
} |
|
|
|
static void AssertAssemblerResult (ShellService.ProcessOutput output) |
|
{ |
|
if (output.ExitCode != 0) |
|
Assert.Fail (output.ToString ()); |
|
} |
|
} |
|
|
|
class CodeDomCompilationService : CompilationService { |
|
|
|
public static readonly CodeDomCompilationService Instance = new CodeDomCompilationService (); |
|
|
|
protected override string CompileFile (string name) |
|
{ |
|
string file = GetCompiledFilePath (name); |
|
|
|
using (var provider = GetProvider (name)) { |
|
var parameters = GetDefaultParameters (name); |
|
parameters.IncludeDebugInformation = false; |
|
parameters.GenerateExecutable = false; |
|
parameters.OutputAssembly = file; |
|
|
|
var results = provider.CompileAssemblyFromFile (parameters, name); |
|
AssertCompilerResults (results); |
|
} |
|
|
|
return file; |
|
} |
|
|
|
static void AssertCompilerResults (CompilerResults results) |
|
{ |
|
Assert.IsFalse (results.Errors.HasErrors, GetErrorMessage (results)); |
|
} |
|
|
|
static string GetErrorMessage (CompilerResults results) |
|
{ |
|
if (!results.Errors.HasErrors) |
|
return string.Empty; |
|
|
|
var builder = new StringBuilder (); |
|
foreach (CompilerError error in results.Errors) |
|
builder.AppendLine (error.ToString ()); |
|
return builder.ToString (); |
|
} |
|
|
|
static CompilerParameters GetDefaultParameters (string name) |
|
{ |
|
return GetCompilerInfo (name).CreateDefaultCompilerParameters (); |
|
} |
|
|
|
static CodeDomProvider GetProvider (string name) |
|
{ |
|
return GetCompilerInfo (name).CreateProvider (); |
|
} |
|
|
|
static CompilerInfo GetCompilerInfo (string name) |
|
{ |
|
return CodeDomProvider.GetCompilerInfo ( |
|
CodeDomProvider.GetLanguageFromExtension (Path.GetExtension (name))); |
|
} |
|
} |
|
|
|
class ShellService { |
|
|
|
public class ProcessOutput { |
|
|
|
public int ExitCode; |
|
public string StdOut; |
|
public string StdErr; |
|
|
|
public ProcessOutput (int exitCode, string stdout, string stderr) |
|
{ |
|
ExitCode = exitCode; |
|
StdOut = stdout; |
|
StdErr = stderr; |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return StdOut + StdErr; |
|
} |
|
} |
|
|
|
static ProcessOutput RunProcess (string target, params string [] arguments) |
|
{ |
|
var stdout = new StringWriter (); |
|
var stderr = new StringWriter (); |
|
|
|
var process = new Process { |
|
StartInfo = new ProcessStartInfo { |
|
FileName = target, |
|
Arguments = string.Join (" ", arguments), |
|
CreateNoWindow = true, |
|
UseShellExecute = false, |
|
RedirectStandardError = true, |
|
RedirectStandardInput = true, |
|
RedirectStandardOutput = true, |
|
}, |
|
}; |
|
|
|
process.Start (); |
|
|
|
process.OutputDataReceived += (_, args) => stdout.Write (args.Data); |
|
process.ErrorDataReceived += (_, args) => stderr.Write (args.Data); |
|
|
|
process.BeginOutputReadLine (); |
|
process.BeginErrorReadLine (); |
|
|
|
process.WaitForExit (); |
|
|
|
return new ProcessOutput (process.ExitCode, stdout.ToString (), stderr.ToString ()); |
|
} |
|
|
|
public static ProcessOutput ILAsm (string source, string output) |
|
{ |
|
var ilasm = "ilasm"; |
|
if (!Platform.OnMono) |
|
ilasm = NetFrameworkTool ("ilasm"); |
|
|
|
return RunProcess (ilasm, "/nologo", "/dll", "/out:" + Quote (output), Quote (source)); |
|
} |
|
|
|
static string Quote (string file) |
|
{ |
|
return "\"" + file + "\""; |
|
} |
|
|
|
public static ProcessOutput PEVerify (string source) |
|
{ |
|
return RunProcess (WinSdkTool ("peverify"), "/nologo", Quote (source)); |
|
} |
|
|
|
public static ProcessOutput PEDump (string source) |
|
{ |
|
return RunProcess ("pedump", "--verify code,metadata", Quote (source)); |
|
} |
|
|
|
static string NetFrameworkTool (string tool) |
|
{ |
|
return Path.Combine ( |
|
Path.GetDirectoryName (typeof (object).Assembly.Location), |
|
tool + ".exe"); |
|
} |
|
|
|
static string WinSdkTool (string tool) |
|
{ |
|
var sdks = new [] { |
|
@"Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools", |
|
@"Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools", |
|
@"Microsoft SDKs\Windows\v7.0A\Bin", |
|
}; |
|
|
|
foreach (var sdk in sdks) { |
|
var exe = Path.Combine (Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), sdk, tool + ".exe"); |
|
if (File.Exists(exe)) |
|
return exe; |
|
} |
|
|
|
return tool; |
|
} |
|
} |
|
}
|
|
|