|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Reflection;
#if NET
using System.Runtime.Loader;
#endif
namespace Microsoft.DotNet.ApiCompat
{
/// <summary>
/// Helper to resolve the Microsoft.CodeAnalysis roslyn assemblies based on a given assemblies path.
/// </summary>
internal sealed class RoslynResolver
{
private readonly string _roslynAssembliesPath;
#if NET
private readonly AssemblyLoadContext? _currentContext;
#endif
private RoslynResolver(string roslynAssembliesPath)
{
_roslynAssembliesPath = roslynAssembliesPath;
#if NET
_currentContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly());
if (_currentContext != null)
{
_currentContext.Resolving += Resolve;
}
#else
AppDomain.CurrentDomain.AssemblyResolve += Resolve;
#endif
}
public static RoslynResolver Register(string roslynAssembliesPath) => new(roslynAssembliesPath);
public void Unregister()
{
#if NET
if (_currentContext != null)
{
_currentContext.Resolving -= Resolve;
}
#else
AppDomain.CurrentDomain.AssemblyResolve -= Resolve;
#endif
}
#if NET
private Assembly? Resolve(AssemblyLoadContext context, AssemblyName assemblyName)
{
return LoadRoslyn(assemblyName, path => context.LoadFromAssemblyPath(path));
}
#else
private Assembly? Resolve(object sender, ResolveEventArgs args)
{
AssemblyName name = new(args.Name);
return LoadRoslyn(name, path => Assembly.LoadFrom(path));
}
#endif
private Assembly? LoadRoslyn(AssemblyName name, Func<string, Assembly> loadFromPath)
{
const string codeAnalysisName = "Microsoft.CodeAnalysis";
const string codeAnalysisCSharpName = "Microsoft.CodeAnalysis.CSharp";
if (name.Name == codeAnalysisName || name.Name == codeAnalysisCSharpName)
{
Assembly asm = loadFromPath(Path.Combine(_roslynAssembliesPath!, $"{name.Name}.dll"));
Version? resolvedVersion = asm.GetName().Version;
if (resolvedVersion < name.Version)
{
throw new Exception(string.Format(CommonResources.UpdateSdkVersion, resolvedVersion, name.Version));
}
// Being extra defensive but we want to avoid that we accidentally load two different versions of either
// of the roslyn assemblies from a different location, so let's load them both on the first request.
if (name.Name == codeAnalysisName)
loadFromPath(Path.Combine(_roslynAssembliesPath!, $"{codeAnalysisCSharpName}.dll"));
else
loadFromPath(Path.Combine(_roslynAssembliesPath!, $"{codeAnalysisName}.dll"));
return asm;
}
return null;
}
}
}
|