Skip to content

Commit

Permalink
Error handling and assembly resolving improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
yevhen committed Dec 19, 2013
1 parent 511bb5b commit bb6f2cf
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 105 deletions.
2 changes: 1 addition & 1 deletion Source/Nake/Nake.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
<Compile Include="..\ProductAssemblyInfo.cs">
<Link>Properties\ProductAssemblyInfo.cs</Link>
</Compile>
<Compile Include="AssemblyResolver.cs" />
<Compile Include="RoslynAssemblyResolver.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="Application.cs" />
<Compile Include="Magic\AnalysisResult.cs" />
Expand Down
65 changes: 40 additions & 25 deletions Source/Nake/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,64 @@
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Security.Permissions;
using System.Threading.Tasks;

using NuGet;

namespace Nake
{
public class Program
{
public static ManualResetEvent Downloading = new ManualResetEvent(false);

[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
public static void Main(string[] args)
{
RegisterResolver();
CatchUnhandledExceptions();

RegisterRoslynResolver();
DownloadRoslynPackage();

StartApplication(args);
}

static void CatchUnhandledExceptions()
{
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
var e = (Exception) args.ExceptionObject;

Log.Error(e);
Exit.Fail(e);
};

TaskScheduler.UnobservedTaskException += (sender, args) =>
{
var e = args.Exception;
args.SetObserved();

DownloadRoslyn();
foreach (var inner in e.Flatten().InnerExceptions)
Log.Error(inner);

StartApplication(args);
Exit.Fail(e);
};
}

static void RegisterResolver()
static void RegisterRoslynResolver()
{
AssemblyResolver.Register();
RoslynAssemblyResolver.Register();
}

static void DownloadRoslyn()
static void DownloadRoslynPackage()
{
const string roslynCompilerAssembly = "Roslyn.Compilers.CSharp";
const string roslynVersion = "1.2.20906.2";

if (GacUtil.IsAssemblyInGAC(roslynCompilerAssembly))
return;

try
{
Assembly.Load(new AssemblyName(roslynCompilerAssembly));
AssemblyResolver.Unregister();

const string roslynLocalPath = @"Roslyn\Roslyn.Compilers.Common.1.2.20906.2\lib\net45\Roslyn.Compilers.dll";
if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, roslynLocalPath)))
return;
}
catch (FileNotFoundException)
{}

Console.WriteLine("Roslyn CTP was not found");
Console.WriteLine("Installing Roslyn CTP ...");
Expand All @@ -58,12 +78,12 @@ static void DownloadRoslyn()

static void StartApplication(string[] args)
{
AssemblyResolver.Resolve = true;
RoslynAssemblyResolver.Resolve = true;

try
{
var application = new Application(Options.Parse(args));
application.Start();
var options = Options.Parse(args);
new Application(options).Start();
}
catch (OptionParseException e)
{
Expand All @@ -76,11 +96,6 @@ static void StartApplication(string[] args)
Log.Error(e.SourceException);
Exit.Fail(e);
}
catch (Exception e)
{
Log.Error(e);
Exit.Fail(e);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,77 +1,81 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Nake
{
class AssemblyResolver
{
public static volatile bool Resolve = false;

static readonly List<RoslynAssembly> roslynAssemblies;
static readonly ConcurrentDictionary<string, Assembly> resolvedAssemblies = new ConcurrentDictionary<string, Assembly>();

static readonly string currentDir;

static AssemblyResolver()
{
currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

roslynAssemblies = new List<RoslynAssembly>
{
new RoslynAssembly("Roslyn.Compilers", "Roslyn.Compilers.Common"),
new RoslynAssembly("Roslyn.Compilers.CSharp", "Roslyn.Compilers.CSharp")
};
}

public static void Register()
{
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
}

public static void Unregister()
{
AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly;
}

static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
if (!Resolve)
return null;

return resolvedAssemblies.GetOrAdd(args.Name, name =>
{
var assemblyName = new AssemblyName(args.Name);

var roslynAssembly = roslynAssemblies
.Find(x => x.AssemblyName == assemblyName.Name);

Unregister();

return roslynAssembly.Load();
});
}

class RoslynAssembly
{
public readonly string AssemblyName;
readonly string assemblyPath;

public RoslynAssembly(string assemblyName, string packageName)
{
AssemblyName = assemblyName;

assemblyPath = Path.Combine(currentDir,
string.Format(@"Roslyn\{0}.1.2.20906.2\lib\net45\{1}.dll", packageName, assemblyName));
}

public Assembly Load()
{
return Assembly.LoadFrom(assemblyPath);
}
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Nake
{
class RoslynAssemblyResolver
{
public static volatile bool Resolve = false;

static readonly List<RoslynAssembly> roslynAssemblies;

static readonly ConcurrentDictionary<string, Lazy<Assembly>> resolvedAssemblies =
new ConcurrentDictionary<string, Lazy<Assembly>>();

static readonly string currentDir;

static RoslynAssemblyResolver()
{
currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

roslynAssemblies = new List<RoslynAssembly>
{
new RoslynAssembly("Roslyn.Compilers", "Roslyn.Compilers.Common"),
new RoslynAssembly("Roslyn.Compilers.CSharp", "Roslyn.Compilers.CSharp")
};
}

public static void Register()
{
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
}

public static void Unregister()
{
AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly;
}

static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
return Resolve ? resolvedAssemblies.GetOrAdd(args.Name, LoadRoslynAssembly).Value : null;
}

static Lazy<Assembly> LoadRoslynAssembly(string name)
{
return new Lazy<Assembly>(() =>
{
var assemblyName = new AssemblyName(name);

var roslynAssembly = roslynAssemblies
.Find(x => x.AssemblyName == assemblyName.Name);

Unregister();

return roslynAssembly.Load();
});
}

class RoslynAssembly
{
public readonly string AssemblyName;
readonly string assemblyPath;

public RoslynAssembly(string assemblyName, string packageName)
{
AssemblyName = assemblyName;

assemblyPath = Path.Combine(currentDir,
string.Format(@"Roslyn\{0}.1.2.20906.2\lib\net45\{1}.dll", packageName, assemblyName));
}

public Assembly Load()
{
return Assembly.LoadFrom(assemblyPath);
}
}
}
}
57 changes: 57 additions & 0 deletions Source/Nake/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,61 @@ public struct PROCESSENTRY32
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
}

public static class GacUtil
{
[DllImport("fusion.dll")]
private static extern IntPtr CreateAssemblyCache(
out IAssemblyCache ppAsmCache,
int reserved);

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae")]
private interface IAssemblyCache
{
int Dummy1();

[PreserveSig()]
IntPtr QueryAssemblyInfo(
int flags,
[MarshalAs(UnmanagedType.LPWStr)] string assemblyName,
ref AssemblyInfo assemblyInfo);

int Dummy2();
int Dummy3();
int Dummy4();
}

[StructLayout(LayoutKind.Sequential)]
private struct AssemblyInfo
{
public int cbAssemblyInfo;
public int assemblyFlags;
public long assemblySizeInKB;

[MarshalAs(UnmanagedType.LPWStr)]
public string currentAssemblyPath;

public int cchBuf;
}

public static bool IsAssemblyInGAC(string assemblyName)
{
var assembyInfo = new AssemblyInfo { cchBuf = 512 };
assembyInfo.currentAssemblyPath = new string('\0', assembyInfo.cchBuf);

IAssemblyCache assemblyCache;
var hr = CreateAssemblyCache(out assemblyCache, 0);

if (hr == IntPtr.Zero)
{
hr = assemblyCache.QueryAssemblyInfo(1, assemblyName, ref assembyInfo);
return hr == IntPtr.Zero;
}

Marshal.ThrowExceptionForHR(hr.ToInt32());
return false;
}
}
}
17 changes: 15 additions & 2 deletions Source/Utility/Exit.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
using System;
using System.Diagnostics;

namespace Nake
{
public static class Exit
{
internal static Action<int, string, Exception> Terminator = (code, msg, ex) => Environment.Exit(code);
internal static Action<int, string, Exception> Terminator = (code, msg, ex) =>
{
if (Debugger.IsAttached)
WaitTermination();

Environment.Exit(code);
};

static void WaitTermination()
{
Console.Write("Press any key to terminate ...");
Console.ReadKey();
}

public static void Ok()
{
Expand All @@ -13,7 +26,7 @@ public static void Ok()

public static void Fail(string message)
{
Environment.Exit(-1);
Terminator(-1, message, null);
}

public static void Fail(Exception exception)
Expand Down
Binary file modified Tools/Nake/Meta.dll
Binary file not shown.
Binary file modified Tools/Nake/Meta.pdb
Binary file not shown.
Binary file modified Tools/Nake/Nake.exe
Binary file not shown.
Binary file modified Tools/Nake/Nake.pdb
Binary file not shown.
Binary file modified Tools/Nake/Utility.dll
Binary file not shown.
Binary file modified Tools/Nake/Utility.pdb
Binary file not shown.

0 comments on commit bb6f2cf

Please sign in to comment.