How to add additional dll to Roslyn analyzer NuGet package? - c#

In short: I referenced additional dll in my code-analyzer. All good with unit-tests & while I debugged analyzer by F5. But when this analyzer was installed as NuGet package to real project, it can't find that additional dll & crashes.
#Optional reading - detailed version of question:
I've wrote my code-analyzer (say MyAnalyzer). It finds some types forbidden to use in client code. And its codeFix replaces these types by allowed replacement-types from my custom dll (say myCustom.dll). I've added this dll as dependency to MyAnalyzer.CodeFixes project & to MyAnalyzer.Test unit tests project of analyzer solution.
And it works fine when started by F5 (at special opened VisualStudio instance) & successfully passes all unit tests.
But when I:
built NuGet package of analyzer (by building MyAnalyzer.Package - a special project from VS template to produce analyzer NuGet packege)
then coppied it to NuGet local feed (local path registered at NuGet manager as packages source)
& then install this NuGet package from that local feed to real project by NuGet manager,
diagnostics rise fine, but when I try use codefix, which should replace some type by one from that myCustom.dll, my analyzer can't find myCustom.dll & throws an exception:
System.IO.FileNotFoundException : Could not load file or assembly 'myCustom, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
I probably add reference to my dll at wrong way (not working with analyzers)? Or may be (its only my assumption) any dependency of NuGet package should be NuGet package too?
#Additional info (may be it matters):
Build output looks Ok: folder with build output contains that my referenced dll.
I copied it as is to NuGet local feed folder.
But cache folder of installed package, which appears after installing package, doesn't contain that myCustom.dll. It contains (except for a few small files) only:
C:\Users\user1\.nuget\packages\myanalyzer\0.0.1\MyAnalyzer.0.0.1.nupkg
& 2 dlls:
C:\Users\user1\.nuget\packages\myanalyzer\0.0.1\analyzers\dotnet\cs\MyAnalyzer.dll
C:\Users\user1\.nuget\packages\myanalyzer\0.0.1\analyzers\dotnet\cs\MyAnalyzer.CodeFixes.dll
And when I try to manually put my dll there - to package cahe folder, analyzer still throws exception.

And when I try to manually put my dll there - to package cahe folder,
analyzer still throws exception.
That is not a right way. The issue describes that you have some wrong old version of the nuget under the global cache. Since you do not change the new release version to another, the old wrong nuget package is the same as the new release one, nuget package manager always install the version under the global cache first and if it does not find the same version of the nuget package by nuget package manager UI, it will then download the specific, different version from the nuget package feed into global cache C:\Users\xxx\.nuget\packages\, and then install it into your project.
You should try the following steps to make a careful check:
1) check your new packed nuget package myanalyzer under the local feed. And you can use 7zip tool to unpack myanalyzer.0.0.1.nupkg.
Check whether the myCustom.dll is under the folder myanalyzer.0.0.1\analyzers\dotnet\cs\.
Also, I found the error is
Could not load file or assembly 'MyCustomDll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies.
I am curious why the dll is not right. And the error is that you have used MyCustomDll.dll rather than MyCustom.dll. You should check your Analyzers file and make sure that use the right named file.
Then, repack the new nuget project, ensure the new release nuget package is right.
2) delete the cache folder under C:\Users\xxx\.nuget\packages\myanalyzer.
If you use packages.config nuget management format, and you should also delete the folder under your solution folder <solution_folder>\packages\myanalyzer.0.0.1.
3) after that, reinstall the right version under package manager UI.
Update
Sorry for that I ignored the pack steps of your analyzer nuget project.
Actually, if the dlls are referenced by your current nuget project, the pack button has no duty to pack these dlls into the nupkg file automatically.
You have to pack it manually, no matter how you did, you have to modify the csproj file.
Solution
Use your target or another is to add these under myanalyzer.package.csproj file:
<ItemGroup>
<None Include ="$(TargetDir)mycustom.dll" pack="true" PackagePath="analyzers/dotnet/cs"></None>
</ItemGroup>
Then, repack the project, clean the nuget caches, then install the new one.
I wonder, why all dlls are present twice at installed NuGet package:
as separate files at packages\myanalyzer\0.0.1\analyzers\dotnet\cs &
as part of archive in .nupkg file. Or all content of installed package
folder, except .nupkg file, is just unpacked .nupkg?
The nupkg is the file is the original version of your nuget package. It is generated by your pack nuget project. And the folder which contains the nupkg is a local nuget feed.
The folder is like nuget.org source. And this folder which contains it is the nuget package source(nuget download nuget files from this path and then download the nupkg and unpack it under C:\Users\xxx\.nuget\packages)
Besides, the C:\Users\xxx\.nuget\packages is the global nuget cache folder.
So it restores the download nuget packages from the local feed, and if you installed the old same version of the myanalyzer nuget package before, it will always install the old same version like 1.0.0 from the cache folder no matter you have packed a new release same 1.0.0 version. Because the version is the same, so VS IDE will judge that there is already has a same nuget package under the cache folder, it always install the old one rather than the new one.
So that is why I recommended that you should delete all cache files under that folder. Avoid VS installing older cache packages all the time. Or you should set a new version for the updated nuget package like 2.0.0.
This is the explanation and the difference between these two Folders.
One is the original local nuget package feed. Another is the storage path and unpacking path of the package downloaded from the local nuget package feed, and it will record the previously downloaded packages. You need to pay attention to this.

Related

How to trace resolving process of nuget package references

I have a solution with multiple projects in it (~400). A lot of them are using newtonsoft.json library. Some of them are using different version of the library. In a host project for web app, there is no direct reference to newtonsoft.json. Also, I believe, there are dependencies to packages that depend on newtonsoft.json. However, in bin folder of that project I began getting newtonsoft.json 9.0 instead of newtonsoft.json 11.0 as it used to be. And it causes load exception in runtime due to invalid version. If I include direct reference to newtonsoft.json 11.0, it still puts version 9.0 in the bin folder even if I clear all bin,obj folders. I use PackageReference for managing dependencies, and everything is in .net 4.6.1; I use binding redirects to resolve issues with different versions of the same library.
My question is if there is a way to diagnose how particular dll of package ref appears in bin folder? I would like to see some sort of comprehensive trace of dependency resolution so that I can fix it without using "trial and error" approach.
Upd.
Actually, thanks guys for pointing to structured logging. You are the best! So the issue was that one of the project had <OutputPath> pointing to the bin folder of the host project. So when the project was built, it was overriding binaries in the host project. Apparently, build order has changed due to continuous reference shuffling and the project with wrong <OutputPath> started building last. To find this out 1) I Newtonsoft.Json was recorded in DoubleWrites section 2) analyzing location from double writes I found out in _CopyFilesMarkedLocal section
Copying file from "\VenomousProject\bin\Debug\Newtonsoft.Json.dll" to "HostProject\bin\Debug\Newtonsoft.Json.dll".
That was it.
You can try MSBuild Binary and Structured Log Viewer (https://msbuildlog.com/)
build the project from command line with msbuild -bl - you will get msbuild.binlog
open the binlog with the log viewer and use the dll name as a search term
inspect all records related to the dll and backtrack a place (project and msbuild' target) where the wrong file version is taken.
With PackageReference, NuGet will write a file named project.assets.json in the project's obj folder. This file is used by the rest of the build to determine what files from packages should be included in the build, but it also contains a list of all the packages that were selected, what version was selected, and what dependencies each package has (package id and version). This is the closest thing to dependency resolving debugging that NuGet has.
In the file, search for "Newtonsoft.Json/, and you should find which version of Newtonsoft.Json that NuGet selected. Remove the / and replace it with ", and you can find all the packages that have dependencies on Newtonsoft.Josn. Search for newtonsoft.json.dll to find all the packages that ship that dll in its package (sometimes package authors perfer to ship multiple dlls in their package, rather than adding dependencies, which prevents NuGet from being able to version selection.
If there are multiple packages with which contain a dll with the same filename, NuGet will tell MSBuild about all of them, and it's up to MSBuild to select which one to use (pass to the compiler and copy to the bin/publish directory). As #Serg wrote in their answer, you can use binlogs (with the -bl argument on any MSBuild command, including dotnet restore or dotnet build). NuGet's inner workings are not output to MSBuild, so when your package graph has multiple packages that list Newtonsoft.Json as a dependency, it won't tell you why NuGet chose a specific version, but binlogs are very useful at debugging other build related issues.

VisualStudio Online and pack Nuget packages

I'm creating a repository in Visual Studio OnLine when I have some class libraries. I want to create (pack) and push for each of them a Nuget package in my private repository.
In a project there is a dependency on Microsoft.ApplicationInsights. If I create on my computer a package, it is working fine. If I try to do the same on Visual Studio Online I have an error:
The nuget command failed with exit code(1) and
error(NuGet.CommandLine.CommandLineException: Unable to find
'Microsoft.ApplicationInsights.2.6.4.nupkg'. Make sure the project has
been built.
This is the full error:
NuGet Version: 4.1.0.2450 Attempting to build package from
'Vu.Common.Logging.AppInsightsEvent.csproj'. MSBuild auto-detection:
using msbuild version '15.6.85.37198' from 'C:\Program Files
(x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\bin'. Use
option -MSBuildVersion to force nuget to use a specific version of
MSBuild. Packing files from
'D:\a\1\s\Vu.Common\Vu.Common.Logging.AppInsightsEvent\bin\Release'.
Using 'Vu.Common.Logging.AppInsightsEvent.nuspec' for metadata. Add
file
'D:\a\1\s\Vu.Common\Vu.Common.Logging.AppInsightsEvent\bin\Release\Vu.Common.Logging.AppInsightsEvent.dll'
to package as 'lib\net462\Vu.Common.Logging.AppInsightsEvent.dll'
Found packages.config. Using packages listed as dependencies
NuGet.CommandLine.CommandLineException: Unable to find
'Microsoft.ApplicationInsights.2.6.4.nupkg'. Make sure the project has
been built. at
NuGet.CommandLine.ProjectFactory.AddDependencies(Dictionary`2
packagesAndDependencies) at
NuGet.CommandLine.ProjectFactory.ProcessDependencies(PackageBuilder
builder) at NuGet.CommandLine.ProjectFactory.CreateBuilder(String
basePath, NuGetVersion version, String suffix, Boolean buildIfNeeded,
PackageBuilder builder) at
NuGet.Commands.PackCommandRunner.BuildFromProjectFile(String path)
at NuGet.CommandLine.PackCommand.ExecuteCommand() at
NuGet.CommandLine.Command.ExecuteCommandAsync() at
NuGet.CommandLine.Command.Execute() at
NuGet.CommandLine.Program.MainCore(String workingDirectory, String[]
args)
My configuration is easy. I added two Nuget, the first it is packing my nuget and the second push in the repository it.
I tried different configuration and also created manually the nuspec file but without result.
Any ideas? What is the right way to pack and deploy nugget packages?
Update
During the build the error I saw is like that:
Then I tried to create the same package on my local folder. I've only removed OutputDirectory, NonInteractive and the initial path. I can create the package without problem.
According to the error message, seems your nuget packages failed to download the referenced nuget packages from url.
Instead of using customize nuget command, you could try to use Package: NuGet extension directly and select Pack NuGet packages and try agian.
Also enable Verbose Debug Mode to get more detail log by add system.debug=true for troubleshooting and narrow down the issue.

msbuild reference assemblies resolve order while building solution with multiple .csproj in visual studio

I have a solution with multiple projects and these projects have nuget packages installed which are different in version. Looks when solution is rebuild, the version of dll copied to output directory is not deterministic and often I end up with runtime "file not found" exception for obvious reason. I will try to over simply the problem with below example :
Let's say ProjectA is cossole application and refers version 1.0.0 of assembly xyz.
ProjectA also refers to ProjectB in same solution via project reference. Now let say ProjectB references version 2.0.0 of assembly xyz.
I would like to know when solution is rebuild, is it deterministic which version of xyz dll will be copied to output directory ? If no, is there a way to ensure this. I know in that case I need to update config with appropriate binding re direction policy.
I would like to know when solution is rebuild, is it deterministic which version of xyz dll will be copied to output directory?
Yes, the version 1.0.0 of xyz dll will be copied to output directory.
According to the document Dependency resolution with PackageReference:
When the package graph for an application contains different versions
of the same package, NuGet chooses the package that's closest to the
application in the graph and ignores all others. This behavior allows
an application to override any particular package version in the
dependency graph.
In you case, the version 1.0.0 of xyz.dll is nearer to the projectA in the graph, that version will be copied to the output directory.
Then I also created a test package xyz with version 1.0.0 and 2.0.0, add those two nuget package to the projectA and projectB, then ProjectA also refers to ProjectB, after build, the version 1.0.0 xyz.dll copied to the output directory:
Update:
Can you check and confirm on the behavior when dll version is
different in ProjectA and ProjectB \
To verify this question, I have created a project xyz, generated the version 1.0.0 xyz.dll and 2.0.0 xyz.dll, then refers 2.0.0 xyz.dll to the project B and 1.0.0 xyz.dll to the project A, build the solution, got the same result: 1.0.0 xyz.dll will be copied to the output directory:
As the above test results, 1.0.0 xyz.dll will be copied to the output directory. When you got the file not found error, please check if you are invoke the method in version 2.0.0 the xyz.dll.
Update2:
screen shot you showing says file version (but file version is not
same as assembly version)
That because I want to be able check the AssemblyVersion in the windows explorer more convenient, I change the AssemblyVersion and AssemblyFileVersion at the same time before gnenerating the different version dll:
Like:
1.0.0 xyz.dll:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
2.0.0xyz.dll:
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
So we just need check the file version instead of assembly version in wondows explorer for more convenient.
Hope this helps.

Nuget assembly not inside a framework folder

I am using NuGetPackageExplorer to publish a nuGet package on our private nuget feed. Before publishing I used menu option Tools->Analyze Package and getting a warning "Assembly not inside a framework folder".
(Note: I also got an error related to lib folder earlier but corrected it by adding lib folder as suggested by NuGetPackageExplorer)
Following is my nuGet package structure:
->lib
MyDll.Dll
I have not mentioned any specific framework in the Package Metadata.
(I also tried NuGet Assembly outside lib folder, but it is talking about "lib" folder)
Does anyone know a solution to this?
If you go through the source-code of nuget, you will find that this error can be ignored if your dll is targeted for multi-frameworks.
One unit test for nuget core seems to prove this.
I finally found how to do this.
In NuGetPackageExplorer, point to Package Contents -> "lib" folder, if you right click on it (lib folder) then a menu appears and that menu has option to add Framework folder.

Could not resolve this reference. Could not locate the assembly "Microsoft.Practices.EnterpriseLibrary.Common"

I have a project that use the nuget package EnterpriseLibrary.Common version 5.0.505.0, but when I restore the package in another computer the next warning message appear.
Could not resolve this reference. Could not locate the assembly
"Microsoft.Practices.EnterpriseLibrary.Common".
I look for the fisical path and I do not found the dll, there is only a xml file. "Microsoft.Practices.EnterpriseLibrary.Common.xml"
It should not be included in the nuget package?
or should be in GAC?
if so, how do I install it?
This is something specific to your environment. Normally if you have the nuget package EnterpriseLibrary.Common version 5.0.505.0 referenced in your solution and you open the solution on a new machine and restore nuget packages, the dll is also restored.
You can try running this in Package Manager Console:
Update-Package EnterpriseLibrary.Common -Reinstall
and hope that it helps. If it does not, the nuget package itself (*.nupkg) should be located in
packages\EnterpriseLibrary.Common.5.0.505.0\EnterpriseLibrary.Common.5.0.505.0.nupkg
Make a copy of this file and change the extension to .zip. Open it with your favorite archive manager (Windows Explorer will do) and find all the dlls it contains in the lib folder inside the archive. Microsoft.Practices.EnterpriseLibrary.Common.dll is one of them.

Categories

Resources