Skip to content

Commit

Permalink
Improve loading of Roslyn assemblies on validate package task (#22277)
Browse files Browse the repository at this point in the history
* Improve loading of Roslyn assemblies on validate package task

* PR Feedback
  • Loading branch information
safern authored and joperezr committed Mar 11, 2022
1 parent 278b73b commit 964e9e5
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.DotNet.PackageValidation;
using Microsoft.NET.Build.Tasks;
using NuGet.RuntimeModel;
#if NETCOREAPP
using System.Runtime.Loader;
#endif

namespace Microsoft.DotNet.Compatibility
{
Expand Down Expand Up @@ -43,14 +45,23 @@ public class ValidatePackage : TaskBase

public override bool Execute()
{
#if NETCOREAPP
AssemblyLoadContext currentContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly());
currentContext.Resolving += ResolverForRoslyn;
#else
AppDomain.CurrentDomain.AssemblyResolve += ResolverForRoslyn;
#endif
try
{
return base.Execute();
}
finally
{
#if NETCOREAPP
currentContext.Resolving -= ResolverForRoslyn;
#else
AppDomain.CurrentDomain.AssemblyResolve -= ResolverForRoslyn;
#endif
}
}

Expand Down Expand Up @@ -103,19 +114,41 @@ protected override void ExecuteCore()
}
}

#if NETCOREAPP
private Assembly ResolverForRoslyn(AssemblyLoadContext context, AssemblyName assemblyName)
{
return LoadRoslyn(assemblyName, path => context.LoadFromAssemblyPath(path));
}
#else
private Assembly ResolverForRoslyn(object sender, ResolveEventArgs args)
{
AssemblyName name = new(args.Name);
if (name.Name == "Microsoft.CodeAnalysis" || name.Name == "Microsoft.CodeAnalysis.CSharp")
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 = Assembly.LoadFrom(Path.Combine(RoslynAssembliesPath, $"{name.Name}.dll"));
Version version = asm.GetName().Version;
if (version < name.Version)
Assembly asm = loadFromPath(Path.Combine(RoslynAssembliesPath, $"{name.Name}.dll"));
Version resolvedVersion = asm.GetName().Version;
if (resolvedVersion < name.Version)
{
throw new Exception(string.Format(Resources.UpdateSdkVersion, version, name.Version));
throw new Exception(string.Format(Resources.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.
Assembly _ = name.Name == codeAnalysisName ?
loadFromPath(Path.Combine(RoslynAssembliesPath, $"{codeAnalysisCsharpName}.dll")) :
loadFromPath(Path.Combine(RoslynAssembliesPath, $"{codeAnalysisName}.dll"));

return asm;
}

return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
<PropertyGroup Condition="'$(RoslynAssembliesPath)' == ''">
<RoslynAssembliesPath>$(RoslynTargetsPath)</RoslynAssembliesPath>
<_packageReferenceList>@(PackageReference)</_packageReferenceList>
<RoslynAssembliesPath Condition="'$(MSBuildProjectExtension)' == '.csproj' and $(_packageReferenceList.Contains('Microsoft.Net.Compilers.Toolset'))">$([System.IO.Path]::GetDirectoryName($(CSharpCoreTargetsPath)))</RoslynAssembliesPath>
<RoslynAssembliesPath Condition="'$(MSBuildProjectExtension)' == '.vbproj' and $(_packageReferenceList.Contains('Microsoft.Net.Compilers.Toolset'))">$([System.IO.Path]::GetDirectoryName($(VisualBasicCoreTargetsPath)))</RoslynAssembliesPath>
<!-- CSharpCoreTargetsPath and VisualBasicCoreTargetsPath point to the same location, Microsoft.CodeAnalysis.CSharp and Microsoft.CodeAnalysis.VisualBasic
are on the same directory as Microsoft.CodeAnalysis. So there is no need to distinguish between csproj or vbproj. -->
<RoslynAssembliesPath Condition="$(_packageReferenceList.Contains('Microsoft.Net.Compilers.Toolset'))">$([System.IO.Path]::GetDirectoryName($(CSharpCoreTargetsPath)))</RoslynAssembliesPath>
<RoslynAssembliesPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$([System.IO.Path]::Combine('$(RoslynAssembliesPath)', bincore))</RoslynAssembliesPath>
</PropertyGroup>

Expand Down

0 comments on commit 964e9e5

Please sign in to comment.