Add dependency to .NET Analyzer project - c#

I've created a .NET project of type Analyzer with Code Fix (.NET Standard) in Visual Studio 2017. The analyzer created by this project type could be deployed as either a NuGet package or a VSIX extension. I used the Generate NuGet Package on Build setting in project properties and it works very smoothly and without any problem.
The thing is, now I want to add some dependencies to the created NuGet package. I know that this is possible through a .nuspec file for other situations. But in this project type, VS creates the NuGet package in bin folders and there is no .nuspec file whatsoever.
So how can I add dependencies to the created NuGet package in the above scenario?

In Visual Studio 2017 using the new .csproj format, the NuGet dependencies are automatically copied into the NuGet package based on the .csproj file's PackageReferences.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;net45</TargetFrameworks>
<DefineConstants Condition=" '$(TargetFramework)' == 'netstandard1.3' ">$(DefineConstants);LIBLOG_PORTABLE</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<!-- /* Package references for .NET Standard 1.3 */ -->
<PackageReference Include="Microsoft.CSharp" Version="4.4.0" />
<PackageReference Include="System.IO.MemoryMappedFiles" Version="4.3.0" />
<PackageReference Include="System.Resources.ResourceManager" Version="4.3.0" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.4.0" />
<PackageReference Include="System.Runtime" Version="4.3.0" />
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
</ItemGroup>
</Project>
So the only thing extra you need to do is add those dependencies from NuGet to your project. This can be done either by
Manually editing the project file (Right-click the project in Solution Explorer and choose Edit <projectName>.csproj)
Installing the NuGet package via NuGet Package Manager
Changing Dependencies by Updating a .nupkg File
Alternatively, since a .nupkg file is just a .zip file with a different extension, you can change its contents:
Unzip it with a standard zip utility to a temporary directory
In the temporary directory, modify the contents of its .nuspec file, adding additional dependencies as appropriate
Zip the contents of the temporary directory again
Rename the new .zip file, giving it the extension .nupkg

Related

MSBuild not able to build Nuget package because it's storing binaries in the wrong folder

I have a Visual Studio solution which has several projects. One of them, which is called Extensions is supposed to build a Nuget package. Problem is that if I use MSBuild to build the solution (by executing msbuild from the command prompt), I get the following error for the Nuget package build task:
"C:\git\repo\dirs.proj" (default target) (1:7) ->
"C:\git\repo\sources\dirs.proj" (default target) (2:5) ->
"C:\git\repo\sources\dev\Sdk\CompanyName.ProductName.Extensions.csproj" (default target) (7:6) ->
(GenerateNuspec target) ->
C:\Program Files\dotnet\sdk\5.0.408\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets(221,5): error : Could not find a part of the path 'C:\git\ess\target\distrib\Debug\Amd64\CompanyName.ProductName.Extensions'. [C:\git\ess\sources\dev\Sdk\CompanyName.ProductName.Extensions.csproj]
Here is the CSPROJ file I have for this project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>CompanyName.ProductName.Extensions</AssemblyName>
<RootNamespace>CompanyName.ProductName.Extensions</RootNamespace>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeCoreAssets</TargetsForTfmSpecificBuildOutput>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<NuspecFile>CompanyName.ProductName.Extensions.nuspec</NuspecFile>
<PackageOutputPath>$(DistribRoot)\$(Configuration)\$(Platform)\$(MSBuildProjectName)</PackageOutputPath>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SkipAssemblyComVisible>true</SkipAssemblyComVisible>
<IncludeBuildOutput>false</IncludeBuildOutput>
<OutputPath>$(DistribRoot)\$(Configuration)\$(Platform)\$(MSBuildProjectName)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<NuspecBasePath>$(OutputPath)</NuspecBasePath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Core\CompanyName.ProductName.Core.csproj" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<Target DependsOnTargets="ResolveReferences" Name="IncludeCoreAssets">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
</Project>
I personally think the issue is that for some reason when MSbuild is trying to create the Nuget package, it's trying to find the Extensions project DLLs in this path: \target\distrib\Debug\Amd64\CompanyName.ProductName.Extensions\, even though it actually built and stored the said binaries in this path earlier: \target\distrib\CompanyName.ProductName.Extensions\. <--- This is something I checked manually myself by examining the 'target' folder after running msbuild.
Visual Studio however doesn't have this issue. When I build this solution within Visual Studio, it stores the binaries for this Extensions project in \target\distrib\Debug\AnyCPU\CompanyName.ProductName.Extensions\ folder, which I think matches the pattern defined in the CSPROJ file:
<OutputPath>$(DistribRoot)\$(Configuration)\$(Platform)\$(MSBuildProjectName)</OutputPath>
Visual Studio also stored the Nuget package in this folder, which is also correct as per the <PackageOutputPath> spec mentioned in the CSPROJ file.
So can someone suggest why MSbuild is storing the built DLLs of this project in \target\distrib\CompanyName.ProductName.Extensions\, but is trying to find them in \target\distrib\Debug\Amd64\CompanyName.ProductName.Extensions\ ?
I think the issue is that for some reason, MSbuild isn't adhering to the <OutputPath> spec mentioned above when it's storing the built DLLs for this project.

MSBuild: Remove Reference to Assembly added via NuGet

In the process of moving some legacy code from packages.config to PackageReference.
I have a NuGet package (let's say A.nupkg) that has a reference to a different NuPkg (B.nupkg). B.nupkg includes a reference to Foo.dll.
A project referenced A.nupkg in packages.config, but B.nupkg was not (despite being a transitive dependency). The problem is that the project references a drop-in replacement (same namespace and classes, but including bug fixes) for the Foo API in the form of a Foov2.dll
Now with the change to PackageReference the transitive dependency is picked up, Foo.dll is referenced by the project and we end up with ambiguous references between Foo.dll and Foov2.dll. I can't change the NuGet package (wish I could) so I need a workaround.
I tried adding a target that removes the unwanted reference before building it, but I can't find the right spot to put it - or maybe references from NuGets are handled different to normal references:
<Target Name="RemoveOutdatedReferences" BeforeTargets="BeforeBuild">
<Message Importance="High" Text="All references: #(Reference->'%(FileName)').
Sadly no Foo.dll so no wonder I can't remove it."/>
<ItemGroup>
<Reference Remove="Foo, Version=1.2.3.4, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" />
</ItemGroup>
</Target>
Alternatively I also tried to remove the whole transitive NuGet package, but using <PackageReference Remove="Foo"/> didn't work either.
It appears like PackageReference Alias feature is designed specifically for scenarios of namespace collisions.
https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#packagereference-aliases
In some rare instances different packages will contain classes in the
same namespace. Starting with NuGet 5.7 & Visual Studio 2019 Update 7,
equivalent to ProjectReference, PackageReference supports Aliases. By
default no aliases are provided. When an alias is specified, all
assemblies coming from the annotated package with need to be
referenced with an alias.
According to our little discussion, the only option so far I see is to create a custom NuGet package which encapsulates A.nupkg without its dependencies:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<PackageId>My.Wrapper.Around.A<PackageId>
<PackageVersion>1.0.0<PackageVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="A" Version="x.y.z">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
According to the Microsoft docs, <PrivateAssets>all</PrivateAssets> should prevent all transitive dependencies from A.nupkg flowing up to the consumer.
And in your target project:
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="My.Wrapper.Around.A" Version="1.0.0" />
<PackageReference Include="Foov2" Version="1.0.0" />
</ItemGroup>
</Project>

dotnet pack for .NET Core project produces .nupkg file with sources

I have a simple .NET Core 2.1 class library project and need to pack it as a Nuget package. For that I'm using this command:
dotnet pack <project>.csproj --output <outputFolder> /p:Configuration=Release
But, I'm getting this warning:
C:\Program Files\dotnet\sdk\2.1.502\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets(202,5): warning NU5100: The assembly 'obj\Release\netcoreapp2.1\<assembly>.dll' is not inside the 'lib' folder and hence it won't be added as a reference when the package is installed into a project. Move it into the 'lib' folder if it needs to be referenced.
I understand that lib folders are for targeting multiple frameworks, but I don't want that. I only want to target .NET Core 2.1.
At the end my .nupkg file is created, but it has all the source code (don't know why) and when used in other projects, they cannot reference the assemblies, because they are inside the bin\Release\netcoreapp2.1 folder.
I've seen some guides to create Nuget packages from .Net Core projects and none of them mention something related to lib folders.
I need to understand if something is missing or if I'm doing something wrong.
Thanks.
Edit: Added the project file and the .nuspec file
Project File:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Company>xxx</Company>
<RootNamespace>xxxx</RootNamespace>
<Product>xxxx</Product>
<Authors>xxxx</Authors>
<NuspecFile>xxxxx.nuspec</NuspecFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="5.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.3" />
</ItemGroup>
</Project>
.nuspec File:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>xxxxxx</id>
<version>1.0.0</version>
<title>xxxx</title>
<owners>xxxx</owners>
<description>xxxx</description>
<authors>xxxx.</authors>
<copyright>Copyright 2019 xxxx.</copyright>
<summary>xxx is a common code library for internal projects.</summary>
</metadata>
</package>
There appear to be a few misunderstandings here.
Firstly, the lib folder in the nupkg is not for multitargetting, but for all dlls that are part of the build. If your package only supports 1 target framework moniker (TFM), then your nupkg will only have a single folder under lib, for example lib/netstandard2.0/MyLib.dll, or possibly lib/netcoreapp2.1/MyLib.dll if your app uses APIs that are part of the .NET Core 2.1 runtime, but not netstandard. If your project only uses APIs that are part of netstandard, there's no benefit to targetting netcoreapp and has potential problems which might cause you issues in the future, even if it works fine today.
Secondly, a simple class library (to me this means the project only contains .cs files and no content files, no build file, the package will only contain the dll and NuGet's own files) shouldn't need to know anything about what's inside a nupkg. Even more complex projects that multitarget don't need to care about the lib folder when using an SDK style project. Just specify your target TFMs in the <TargetFrameworks> element, and let the SDK pack the nupkg itself. It knows what to do. If you've done anything in your csproj to try to force the output dll to a different location within the nupkg, it's more likely to cause problem than improve things.
Without seeing your .csproj, I cant' guess what you could have done to get that warning message on pack, but like I said, a brand new dotnet new classlib packs just fine, and if your project only contains code files, you shouldn't need anything in the csproj related to pack paths.

Empty Nuget package for just dependencies

I have a project I'd like to put all my dependencies into one nuget package. The idea is that when I need to I can just pull in that one nuget package instead of 10 that I require. Is this possible to do?
When I created a library I removed the class file and just pulled in the dependencies but could not get a package to create.
You can do this by setting the IncludeBuildOutput property to false while creating the package.
For example [using VS 2017] -
File > New Project > .NET Core class library
Right click on project and edit csproj to have the following content -
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
<PackageId>MetaPackage</PackageId>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="NuGet.Versioning" Version="4.7.0-rtm.5104" />
<PackageReference Include="NUnit" Version="3.10.1" />
</ItemGroup>
</Project>
Right click and build project
Look at the package at <project_dir>\bin\Debug\MetaPackage.1.0.0.nupkg
This will create a package that has no \lib and the nuspec file should have package reference dependencies.
You can read more about IncludeBuildOutput here.

How to include a library in .NET Core 2.0

I don't know much about .NET yet, so I guess I'm missing something obvious.
I created a library (targeted as a DLL file, set for .NET standard 2.0), packaged it both as a DLL file and as a NuGet package. Now I want to use the library in another project, on ASP.NET Core 2.0. How should I do it?
I am currently on a Linux VM, so I use Visual Studio Code, and therefore I would prefer some solution without using the full Visual Studio. I tried some solutions using the full Visual Studio, but that didn't work for me, because I haven't found a reference explorer anywhere.
You would have to reference your library in the .csproj file:
An empty .csproj file would look like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
Now, you can have two types of references:
Project Reference - You have a project that serves as a class library in your solution and you want to reference it directly:
<ProjectReference Include="..\..\src\mylib.csproj" />
Package Reference - You have a link to a NuGet package:
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.2" />
Inside your .csproj file, the references should be inside an "ItemGroup" block, and each reference type should have its own "ItemGroup".
Here's an example of a .csproj file with some package references and some project references:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\mylib.csproj" />
<ProjectReference Include="..\..\src\mylib2.csproj" />
</ItemGroup>
</Project>
A lot of people recommend one of two solutions:
Copy the library into your solution folder.
cp -r foo/foo ./foo
dotnet sln add foo/foo.csproj
cd bar
dotnet add reference ../foo/foo.csproj
This is a terrible solution.
Don't do this (i.e., copy and paste your library code every time you want to use it. It is bad for obvious reasons).
Setup a local NuGet repository, copy your library into the local repository, and then add it.
nuget add -name "Local" -source /home/doug/packages
nuget add ~/foo/foo.nupkg -source /home/doug/packages
Then install the package:
cd bar
dotnet add package foo
This is an acceptable solution, but the workflow is quite irritating if you are actively working on your library (foo), because the -source path must be absolute.
--
I recommend you look at dotnet add package with local package file, which explains how you can have a local cache of any custom .nupkg files you want to work with.
Basically, just drop this into your solution folder:
File NuGet.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="local" value="./packages" />
</packageSources>
</configuration>
(Notice that ./packages is a relative path, that will work even when you check your project out on an entirely different machine or OS.)
Now if you call dotnet add package X it will also look for any file called x.nupkg in your ./packages/ folder.
Now if you want to use any custom local library, all you need to do is:
cp ~/foo/foo.nupkg ./packages
cd bar
dotnet add package foo
(Note: by default NuGet caches your .nupkg files in ~/.nuget and will restore packages from that folder if you call dotnet add package X, even if you have a different X.nupkg in your local ./packages folder. You may find the command dotnet nuget locals all --clear useful if you encounter strange behaviour to ensure you're getting the exact version of the .nupkg file you want, not some arbitrary cached version)
Another way to reference the local package in the .csproj file:
<ItemGroup>
<Reference Include="MyAssembly">
<HintPath>path\to\MyAssembly.dll</HintPath>
</Reference>
</ItemGroup>
Given that the DLL file you want to reference in the new ASP.NET Core 2.0 project is relatively fresh, I suspect you will need to make changes to this original DLL file as you develop the ASP.NET project.
In this situation I would add the original DLL project as part of the ASP.NET solution so you can work on both sets of source code, including setting of breakpoints within the same solution workspace.
NuGet packaging of the original DLL project can be delayed until the first release of your whole combined solution has stabilised and you want to make that DLL file available to a larger developer audience beyond the scope of your ASP.NET application.
A good solution will be to add the library (.dll file) that you want to use to the Project's References of your project in which you want to use the library:
Right Click on the project → Add → Reference → Project → Browse → Path_to_your_generated_library (.dll)
This will automatically generate the following node in the .csproj file:
<ItemGroup>
<Reference Include="DotNetCoreClassLibraryCodeParser">
<HintPath>..\..\DotNetCoreClassLibrary\DotNetCoreClassLibrary\bin\Debug\netcoreapp2.1\DotNetCoreClassLibrary.dll</HintPath>
</Reference>
</ItemGroup>

Categories