Xamarin Public Jenkins (auto-signing) 6123a772ed Imported Upstream version 5.8.0.88
Former-commit-id: 4b7216ffda08448e562271ce733688e761120fc5
2017-11-28 19:36:51 +00:00

384 lines
9.9 KiB
C#

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Reflection;
using NUnit.Framework;
#if NET_CORE
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Emit;
using CS = Microsoft.CodeAnalysis.CSharp;
using VB = Microsoft.CodeAnalysis.VisualBasic;
#endif
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;
}
}
public static class Platform {
public static bool OnMono {
get { return TryGetType ("Mono.Runtime") != null; }
}
public static bool OnCoreClr {
get { return TryGetType ("System.Runtime.Loader.AssemblyLoadContext, System.Runtime.Loader, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") != null; }
}
static Type TryGetType (string assemblyQualifiedName)
{
try {
// Note that throwOnError=false only suppresses some exceptions, not all.
return Type.GetType(assemblyQualifiedName, throwOnError: false);
} catch {
return 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")
#if NET_CORE
return RoslynCompilationService.Instance.Compile (name);
#else
return CodeDomCompilationService.Instance.Compile (name);
#endif
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)
{
#if !NET_CORE
var output = Platform.OnMono ? ShellService.PEDump (name) : ShellService.PEVerify (name);
if (output.ExitCode != 0)
Assert.Fail (output.ToString ());
#endif
}
}
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 ());
}
}
#if NET_CORE
class RoslynCompilationService : CompilationService {
public static readonly RoslynCompilationService Instance = new RoslynCompilationService ();
protected override string CompileFile (string name)
{
var compilation = GetCompilation (name);
var outputName = GetCompiledFilePath (name);
var result = compilation.Emit (outputName);
Assert.IsTrue (result.Success, GetErrorMessage (result));
return outputName;
}
static Compilation GetCompilation (string name)
{
var assemblyName = Path.GetFileNameWithoutExtension (name);
var source = File.ReadAllText (name);
var tpa = BaseAssemblyResolver.TrustedPlatformAssemblies.Value;
var references = new []
{
MetadataReference.CreateFromFile (tpa ["netstandard"]),
MetadataReference.CreateFromFile (tpa ["mscorlib"]),
MetadataReference.CreateFromFile (tpa ["System.Private.CoreLib"]),
MetadataReference.CreateFromFile (tpa ["System.Runtime"]),
MetadataReference.CreateFromFile (tpa ["System.Console"]),
MetadataReference.CreateFromFile (tpa ["System.Security.AccessControl"]),
};
var extension = Path.GetExtension (name);
switch (extension) {
case ".cs":
return CS.CSharpCompilation.Create (
assemblyName,
new [] { CS.SyntaxFactory.ParseSyntaxTree (source) },
references,
new CS.CSharpCompilationOptions (OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release));
case ".vb":
return VB.VisualBasicCompilation.Create (
assemblyName,
new [] { VB.SyntaxFactory.ParseSyntaxTree (source) },
references,
new VB.VisualBasicCompilationOptions (OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release));
default:
throw new NotSupportedException ();
}
}
static string GetErrorMessage (EmitResult result)
{
if (result.Success)
return string.Empty;
var builder = new StringBuilder ();
foreach (var diagnostic in result.Diagnostics)
builder.AppendLine (diagnostic.ToString ());
return builder.ToString ();
}
}
#else
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)));
}
}
#endif
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)
{
#if NET_CORE
return Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Windows), "Microsoft.NET", "Framework", "v4.0.30319", tool + ".exe");
#else
return Path.Combine (
Path.GetDirectoryName (typeof (object).Assembly.Location),
tool + ".exe");
#endif
}
static string WinSdkTool (string tool)
{
var sdks = new [] {
@"Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7 Tools",
@"Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools",
@"Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools",
@"Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools",
@"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 pgf = IntPtr.Size == 8
? Environment.GetEnvironmentVariable("ProgramFiles(x86)")
: Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
var exe = Path.Combine (
Path.Combine (pgf, sdk),
tool + ".exe");
if (File.Exists(exe))
return exe;
}
return tool;
}
}
}