Creating one NuGet package from multiple projects in one solution - c#

I have a solution that I'm working on that contains 4 class library projects (A, B, C, D). A and B could be considered the top level projects in the solution. Both A and B reference C, and D stands alone.
These four projects represent a group of services that I have made that handle an automated workflow. They are all closely related, and will only be used in one location (the service manager) so I don't want to split them into different solutions.
My problem is that I want to create a single NuGet package that will contain all 4 libraries, without having to build them all and gather up their DLLs manually. I know that I could technically achieve this by having either A or B reference the remaining projects, but that's not a true relationship and I feel it should be avoided.
I've done a lot of searching on this problem and I can't find a solution other than manually collecting the DLLs and building the package myself. Is there a way to achieve the result that I want using NuGet's features/abilities?
NOTE: In case the tags don't make it clear I'm currently using VS2010 with a TeamCity build server. In case it's relevant I'm also using Git through a Stash server.
EDIT: I just realized this might be important enough to mention. These projects do reference other NuGet packages that I will need to mark as dependencies.

If you have downloaded NuGet.exe
You can run: nuget pack Myproject.csproj -IncludeReferencedProjects and this should include all of your projects. Here's a note from the NuGet docs:
If the project references other projects, you can add the referenced projects as part of the package, or as dependencies with -IncludeReferencedProjects option. This is done recursively. For example, suppose you have project A.csproj, which references B.csproj and C.csproj, while B.csproj references D.csproj & E.csproj, C.csproj references F.csproj & G.csproj. Then, when you run:
nuget pack A.csproj -IncludeReferencedProjects
the generated package will contain files from projects B, C, D, E, F & G, in addition to files from project A.
If a referenced project has a corresponding nuspec file with the same name, then that referenced project is added as a dependency instead. Using the same example, suppose now there is file C.nuspec in the same directory as project file C.csproj. When you run:
nuget pack A.csproj -IncludeReferencedProjects
the generated package will contain files from projects B, D, E, in addition to files from project A, and the package has dependency on C.
Please also see the Command line reference.

You have to define your own nuspec manifest. You can list containing assemblies in files section:
<file src="A\bin\Release\A.dll" target="lib\net40" />
<file src="B\bin\Release\B.dll" target="lib\net40" />
...
For more details read NuSpec reference.
Then reference that nuspec file in NuPack build step instead of proj.

2020 UPDATE
I personally prefer to use the dotnetcommand wherever possible.
The solution that works for me is changing the default project reference in the main project <ProjectReference></ProjectReference> as below:
<ItemGroup>
<ProjectReference Include="..\{ProjectFolder}\{ProjectName}.csproj">
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
<IncludeAssets>{ProjectName}.dll</IncludeAssets>
</ProjectReference>
</ItemGroup>
This solution is confirmed to work when targeting netstandard2.0 and above.

Microsoft does not recommend multiple assemblies in one package. But if you really want to, then do this:
Open main project file and replace this:
<ItemGroup>
<ProjectReference Include="..\SecondClassLibrary\SecondClassLibrary.csproj" />
</ItemGroup>
on this:
<ItemGroup>
<ProjectReference Include="..\SecondClassLibrary\SecondClassLibrary.csproj" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="BuildOnlySettings;ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'All'))" />
</ItemGroup>
</Target>
Source: https://github.com/NuGet/Home/issues/3891

this probably will solve this exact:
nuget.exe pack proj.csproj -IncludeReferencedProjects
you can see:
Create nuget package for a solution with multiple projects

If you have more than one project in VS and your Pack file is dependant on another project then both need to be packed. Pack the dependency first, then pack your project. When adding the package using NuGet Package manager make sure your source is not nuget.org but instead your testing folder. Unless you have uploaded both to Nuget.org.
How do I install a NuGet package .nupkg file locally?

Related

Can I create a Nuget package for a C# project that includes its project dependencies without also creating separate packages for those dependencies? [duplicate]

I want to run a local/internal NuGet repository. I think I've figured out how to "reuse" existing NuGet packages by including them in a dummy project using NuGet and scanning the package file to grab my locally-cached .nupkg files, but...
How do you create a nuget package (.nupkg) from a project, automatically including all dll dependencies and not just those grabbed via NuGet?
Specifically:
Create a solution
Add a new Project
Add references to various .dll files/other projects <-- this is the missing part
Add NuGet packages via package manager / cmdline / whatever
something automatically creates the .nupkg
From what I've found, you're supposed to do things like
manually edit your .csproj file to add <BuildPackage>true</BuildPackage> to include dependencies
manually create a .nuspec file and manually list your dependencies (similar ?)
manually run nuget pack on your .nuspec file
But everything is manual, which is stupid. Even the semi-automatic solutions are still awkward or half-manual:
Create .nuspec templates - doesn't seem to include dependencies, just metadata
nuget pack via build-event (step #5), which you need to add manually to every project, and it has its own quirks:
"$(SolutionDir).nuget\NuGet.exe" pack "$(ProjectPath)" -Properties Configuration=Release
move /Y *.nupkg "$(TargetDir)"
I'll settle for something that automatically creates a .nuspec manifest from project references. Then theoretically that + the nuget build-event can be rolled up into a build-project/nuget package, which is what I really want to see.
Your point #3 (Add references to various .dll files/other projects <-- this is the missing part) really contains two different issues: (1) add references to various dll files, and (2) add references to other projects in the same solution.
Number (2) here has gotten some added support as of NuGet 2.5. You can add an option to include references to other projects in the same solution when creating a NuGet package for a project:
nuget pack projectfile.csproj -IncludeReferencedProjects
If projectfile.csproj references any other projects in your solution that also is exposed as NuGet packages, these projects' NuGet packages will be added as dependencies.
If it references projects in your solution that doesn't expose themselves as NuGet packages, their dlls will be included in this NuGet package.
As for (1), if you find yourself often adding dlls to your projects that aren't available as NuGet packages, you could just create your own (internal) NuGet packages with these files. If you then add these dlls as a NuGet package instead of the files directly, this NuGet package will be a dependency in your project's NuGet package.
I found a well-written article on this topic. I have the same issue with certain packages that have a hierarchy of dependencies and up until now I've been uploading each as a separate NuGet package (what. a. waste. of. time)
I've just tested the solution found here: https://dev.to/wabbbit/include-both-nuget-package-references-and-project-reference-dll-using-dotnet-pack-2d8p
And after examining the NuGet package using NuGet Package Explorer, the DLLs produced by referenced projects are indeed present. I'm going to test by actually submitting this package to NuGet and testing it.
Here's my source in case it is helpful to you: https://github.com/jchristn/NuGetPackTest
And the test NuGet package: https://www.nuget.org/packages/NuGetPackTest/1.0.0
The solution appears to work well. I don't know what it's going to look like when there are layers of references, I'm sure it could get really hairy and really fast.
.csproj from NuGetPackTest library which references project TestLibrary (portions removed for brevity)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp3.0;netcoreapp3.1;net461</TargetFrameworks>
...
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- added this line -->
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<ItemGroup>
<!-- modified this ProjectReference to include the children ReferenceOutputAssembly and IncludeAssets -->
<ProjectReference Include="..\TestLibrary\TestLibrary.csproj">
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
<IncludeAssets>TestLibrary.dll</IncludeAssets>
</ProjectReference>
</ItemGroup>
<!-- added this section -->
<Target DependsOnTargets="ResolveReferences" Name="CopyProjectReferencesToPackage">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))"/>
</ItemGroup>
</Target>
</Project>
For other Googlers, you can use this if you are using the NuGet.targets file to run NuGet Pack:
<Target Name="PrePackage" BeforeTargets="BuildPackage">
<PropertyGroup>
<BuildCommand>$(BuildCommand) -IncludeReferencedProjects</BuildCommand>
</PropertyGroup>
</Target>
Check this out!
The solution which I found is an extension for Visual Studio:
https://visualstudiogallery.msdn.microsoft.com/fbe9b9b8-34ae-47b5-a751-cb71a16f7e96/view/Reviews
You simply add new project called NuGet Package:
Then you are adding interesting you projects to references and BOOOM !!
All dependencies and file directories are automatically added.
If you want to modify NuSpec data you click right at project and go to Properties,
then modify what you want.
Generated NuSpec and nupkg will be in folder obj of your new project.
I hope it helps ;).
I solved this for my case by adding the whole TargetDir to the nuget package.
Just add this to the .csproj :
<Target Name="IncludeAllFilesInTargetDir" AfterTargets="Build">
<ItemGroup>
<None Include="$(TargetDir)\**">
<Pack>true</Pack>
<PackagePath>tools</PackagePath>
</None>
</ItemGroup>
</Target>

How to control output of a nuget package dependencies during build

I would like to support backward compatibility in my application.
Simply saying - one app needs to work using different versions of a dll depending on a flag which the app get's during runtime.
I've simplified everything and created a test solution with 2 projects in it.
Each project has it's own version of the same nuget package.
I picked System.Drawing.Common cause it has no dependencies.
ClassLibrary1 contains System.Drawing.Common of version 4.5.0.
ClassLibrary2 contains System.Drawing.Common of version 6.0.0.
Both projects have same output path:
<OutputPath>..\DEBUG\</OutputPath>
When I build my solution I get just one System.Drawing.Common.dll in my output folder:
Cause both dlls have one name and only version is different.
The desired behavior on the pictures below:
Distribute the nuget package dependencies into different folders according to versions.
Add suffix to the nuget package dependencies according to versions.
The idea is in controlling output of the nuget package dependencies.
Do you have any idea how I can achieve that ?
P.S. all other logic - resolving dependencies according versions etc is out of scope of this question.
It's possible.
First you need to add GeneratePathProperty to PackageReference element in csproj file
<ItemGroup>
<PackageReference Include="System.Drawing.Common">
<Version>4.5.0</Version>
<GeneratePathProperty>true</GeneratePathProperty>
</PackageReference>
</ItemGroup>
It allows us using $(PkgSystem_Drawing_Common) variable which contains a path to the nuget package.
Then we need to create a msbuild targets file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyNugetDll" BeforeTargets="BeforeCompile" Outputs="System.Drawing.Common.dll">
<XmlPeek XmlInputPath="$(ProjectPath)" Query="Project/ItemGroup/PackageReference[#Include='System.Drawing.Common']/Version/text()">
<Output TaskParameter="Result" PropertyName="NugetPackageVersion" />
</XmlPeek>
<ItemGroup>
<NugetrDll Include="$(PkgSystem_Drawing_Common)\lib\net461\System.Drawing.Common.dll" />
</ItemGroup>
<Message Text="Copying #(NugetrDll) to $(OutDir)" Importance="high" />
<Exec Command="copy $(PkgSystem_Drawing_Common)\lib\net461\System.Drawing.Common.dll $(OutDir)\System.Drawing.Common.$(NugetPackageVersion).dll" />
</Target>
</Project>
Here using xpath we select version from project.assets.json file and save it in NugetPackageVersion variable. Exec copy is used to copy the dll to a specific location with a specific prefix which contains a value from NugetPackageVersion variable.
Lastly you need to include msbuild targets file to a project
<Import Project="CopyDll.targets" />
This just isn't how package resolution works in .NET, you get one version of each package which is decided at restore time.
There may be some funky options if you have a very niche problem, but it sounds like maybe you're trying to solve a common problem in an uncommon way which is generally a bad idea.
Typically for the problem of backwards compatibility the onus is on the publisher of the library rather than the consumer of the library to make sure it all works by not making breaking API changes.

Specifying files to add to a nuget package in .csproj file

I am creating a nuget package from some code, but also need to deploy some tools with the package.
In a .nuspec file, I can do this with the <files> element, and this all works well.
However when using a .nuspec file, the packageReferences from the csproj file aren't included, and I am seeing some problems when including them manually (with the <dependencies> element).
The package created also always seems to restore as a .net framework package, even though it is targetting .net, as in this question.
I am hoping that all these problems would go away if I moved to using the .csproj format for specifying the nuget package details, but having read the docs I can't find out how to do it.
Does anyone know how it is done?
If not, can anyone shed any light on created a .net framework / .net core nuget package from a .nuspec file, that restores to the correct target version and respects package dependencies?
It's not easy to find/discover, but NuGet's MSBuild tasks docs page has a section called "including content in a package", which tells you about the PackagePath metadata on MSBuild items, that NuGet uses to copy files into the package.
So, in your csproj, you could have something like this:
<ItemGroup>
<None Include="..\MyTool\Tool.exe" PackagePath="tools" Pack="true" />
</ItemGroup>
and then your package will contain tools\Tool.exe. The Pack="true" attribute is required for None elements.
You can use MSBuild's globbing to copy entire directories, if that's easier. Include="..\MyTool\*". My MSBuild skills are not so advanced, so I don't know how to glob ..\MyTool\**\*, which means all files in all subdirectories, while maintaining the correct directory layout in the PackagePath="???" metadata. So the best I can suggest is one glob per directory.

In VS2019 nuget doesn't consider referenced projects as lib, but as nuget packages

I'm using VS2019 community edition.
I have two .net core projects X, Y, X is referencing Y, and I want to package X as a Nuget package, I'm using the package feature in VS2019.
when I try to add X's Nuget package in another project it searches for Y as a Nuget package and not as DLL should be in X.
How can I change this so Y will be added as DLL in X's package?
I tried to add the following to X's project (.csproj):
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
But I still get the same result, nuget.exe tries to restore Y as Nuget package not as DLL comes with X's package.
UPDATE:
Also if Y has another Nuget dependency, it should be considered in X without referencing it directly in X.
I tried the following:
In X's project:
Go to Dependencies->Projects.
Right click on Y's project then Properties.
Set Private Assets to All
Now this will add Y's dll file in the lib folder in the X's nuspec file.
This will work fine if Y doesn't depend on any other Nuget packages that X doesn't know about, because those packages will not be mentioned as dependencies in X's nuspec file.
Finally I settled down on this:
Just reference Y, generate package for it too and live with it :'(
In VS2019 nuget doesn't consider referenced projects as lib, but as
nuget packages
Actually, the new nuget feature which you used and also PackagePath="lib" function will treat the dll as a nuget dependency rather than an assembly dll.
If you simply want Y to be an Assembly DLL rather than a nuget package, these methods won't work. I have tried these methods, and it really made me struggling.
After a deep research, I found that the problem is that dotnet pack will add the referenced project as a nuget pacakge into the main nuget package automatically. Because dotnet pack will automatically generates an Nuspec file to package the project according to its rules, which treat the referenced project as a Nuget package by default. You can open the X.nupkg file and will see like this under X.nuspec file:
However, in this pack mechanism, we do not have the right to change the rule.
Suggestion
1) when you create the X.nupkg file using Pack Button in VS2019, please do some changes to X.nupkg manually.
Use zip to open nuget compressed package, then open X.nuspec file and delete these node:
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="Y" version="1.0.0" exclude="Build,Analyzers" />
</group>
</dependencies>
Save the changes and then use the modified X.nupkg.
2) Or you should create a custom nuspec file based on our needs and rules manually to guidance the nuget pack process.
Try to use this nuspec file:
<?xml version="1.0"?>
<package >
<metadata>
<id>xxx</id>
<version>1.0.0</version>
<title>xx</title>
..................
</metadata>
<files>
<file src="bin\xxx\xxx\Y.dll" target="lib\xxx(targetframework)"/>
</files>
</package>
Also if Y has another Nuget dependency, it should be considered in X
without referencing it directly in X.
So far, Nuget does not have the feature to ask the main project whether to use the dependency package dll of the referenced project.
And the dependencies from the referenced projects always exist in the main project.
Besides, if you still want these, you could report these problems on our Team Forum and I hope the Team will check them carefully and give you a satisfactory reply.

How to create symbols for multi-project Nuget package?

So I'm really struggling to figure this out.
I have a solution with a few projects, say, A, B, and C. A references and uses B and C (both project references). I wanted to create a Nuget package from A, and I did so, successfully, although I had to jump a few hoops, using things like this:
<ProjectReference Include="..." PrivateAssets="All" />
...
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'All'))" />
</ItemGroup>
</Target>
However, I would like to also debug through my Nuget package, so I found a few tricks to do so. One would be to copy the pdb into the nupkg, although that's not recommended. It supposedly works with this line in your csproj file. That adds the pdb to the nupkg, but only A.pdb is added.
Otherwise one could/should create a A.symbols.nupkg file. I tried doing that using tags like DebugSymbols, DebugType, EmbedSources, and IncludeSymbolsInPackage, but to no avail.
I also tried > dotnet pack A.csproj --include-symbols --include-source, and that generates a A.nupkg and a A.symbols.nupkg, but within the symbols, only A.pdb and not B.pdb and C.pdb.
So I guess my question is: How to create a nuget package from a multi-project solution which you can debug into (so including either pdb's or symbols/sources, not one, but from all referenced projects)?
TLDR: I've already seen a lot of potential solutions, but so far, none worked.
Nuget git issue thread
StackExchange thread
Some blog on GeekLearning.io
And many more...
Some of my last resorts may be: just adding all contents of B and C to A, so that there's only one project left, or creating a powershell script to manually add all the pdb's in the the A.symbols.nupkg. Of course, I'd like a more straightforward solution, if there is any.
The steps that should work:
Ensure that the references are indeed project references and not referencing the built DLLs directly.
Ensure that all of the PDBs are being copied to the output folder after building.
Use either of the methods you have tried (symbols package or hardcoding the inclusion of the .pdb extension).
If this is not working as expected, I would recommend providing additional details such as the csproj file for 'A' and any nuspecs tried.

Categories

Resources