I am working on source generator and I have problems with dependencies:
It will not contribute to the output and compilation errors may occur as a result. Exception was of type 'FileNotFoundException' with message 'Could not load file or assembly 'Flurl.Http, Version=3.0.1.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.'
There is a lot of information on how to pack dependencies into nuget, but I reference analyzer project directly like this:
<ProjectReference Include="SG.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
In analyzer project I added <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
and all dependencies are available in output directory, but VS is not using that directory - it uses AppData\Local\Temp\VBCSCompiler\AnalyzerAssemblyLoader\[...]
instead and it copies only one DLL there.
What can be done to make that work?
I found the way to make it work more or less reliably with some hacks.
Before that I also tried ILMerge, but it didn't work (missing method exceptions).
First of all I embeded dependencies in source generator assembly like this:
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" GeneratePathProperty="true" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(PKGNewtonsoft_Json)\lib\netstandard2.0\Newtonsoft.Json.dll" Visible="false" />
</ItemGroup>
Then I created AssemblyResolve
handler for AppDomain
(static constructor in generator class) like so:
AppDomain.CurrentDomain.AssemblyResolve += (_, args) =>
{
AssemblyName name = new(args.Name);
Assembly loadedAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().FullName == name.FullName);
if (loadedAssembly != null)
{
return loadedAssembly;
}
string resourceName = $"Namespace.{name.Name}.dll";
using Stream resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
if (resourceStream == null)
{
return null;
}
using MemoryStream memoryStream = new MemoryStream();
resourceStream.CopyTo(memoryStream);
return Assembly.Load(memoryStream.ToArray());
};