Build NuGet Package automatically including referenced dependencies - visual-studio

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>

Related

Create NuGet package that when installed copies content to $(OutputPath) regardless of machine

I am trying to get a content file that is included with my NuGet package to be copied to the $(OutputPath) (in my case bin\Debug\netcoreapp3.1 where all the DLLs end up) of a target C# project when the target is built. This needs to work on developers' boxes and on build machines. Here's what I have so far.
I created a NuGet package project with a content node
<ItemGroup>
<Content Include="testsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
I am using the default values for the pack target inputs IncludeContentInPack and ContentTargetFolders, which are true and content;contentFiles respectively.
When I build the package, I get the following structure where testsettings.json appears in contentFiles\any\netcoreapp3.1 and content folders of the .nupkg.
When I install the package into a target .csproj,
the testsettings.json file is added to the project's root folder as expected
the <PackageReference Include="..." Version="..."/> line is added to the target .csproj as expected
no entry appears in the target .csproj for testsettings.json
When I build the target project, the DLLs go to $(OutputPath) but testsettings.json is not copied there, which makes sense because the default for a Content node's action is None). However, I need it to act like <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>.
I thought, "OK I will manually change testsettings.json using the Properties window to Copy if newer":
This adds a node to the target .csproj that looks like:
<ItemGroup>
<Content Update="C:\redacted\path\to\nuget\cache\package.name\N.N.N\contentFiles\any\netcoreapp3.1\testsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
which works great for my machine. This will fail for any build machine and any other developer's machine because that path is specific to my machine.
How can I create the package so that when installing the package it will result in a repeatable way of getting testsettings.json in the right place?
In the course of researching this, I noticed that testhost.exe and testhost.dll somehow magically end up in the right spot even though their source NuGet package's .nuspec and structure are similar to mine. This has left me at bit of a loss on what I need to do to modify my NuGet package's .nuspec, .nupkg, build sequence, etc.
Preferable: Is there a way to do this, and if so how? Highly not preferable: Is there a manual change I can make in the target project such that I can get a reliable way of copying the file that works everywhere?
<ItemGroup>
<Content Include="XXXXX.json">
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
</ItemGroup>
should meet your requirements.
In your NuGet project's xxproj file, add codes like above and use <PackageCopyToOutput>.
In the new project which you'd like to test, before installing your NuGet Package, remember to clear the NuGet Package cache first, and then install your NuGet package and build your project, check if the XXXXX.json file has been copied to Output.
Similar thread: Copy JSON file to bin from class library nuget package.

Restore nuget package in different folder

We have some projects (Plugins) which use in several projects, the output of these projects will copy to the specific folder in the target projects (Plugins folder).
We pack project with Visual studio 2019 Pack option and after that, we push npkg files to our local NuGet server for further use.
The problem is when we want to get these packages, Package Manager should put lib files in the Plugins folder, but unfortunately, the package manager extracts these in the root folder (bin).
My question is: How can I config nuspec file to force package manager to extract to the right folder, and can I do it with visual studio or I have to create nuspec file manually.
You should use a <packages_id>.props file to realize it.
create a file called <packages_id>.props under the build folder of your lib project.
You should note that if your nupkg file is called Plugins.1.0.0.nupkg, you should name this file as Plugins.props so that it will work.
add these on Plugins.props file:
<Project>
<Target Name="CopyFiles" BeforeTargets="Build">
<ItemGroup>
<File Include="$(MSBuildThisFileDirectory)..\Plugins\*.*"></File>
</ItemGroup>
<!--It will copy the plugins output files into the Plugins folder of the goal project-->
<Copy SourceFiles="#(File)" DestinationFolder="$(ProjectDir)Plugins"></Copy>
</Target>
</Project>
add these on Plugins.csproj file:
<ItemGroup>
<None Include="bin\Debug\netstandard2.0\Plugins.dll" Pack="true" PackagePath="Plugins"></None>
//add any output files from Plugins project which you want them to be packed
<None Include="build\Plugins.props" Pack="true" PackagePath="build"></None>
</ItemGroup>
use Pack Button to create the new release version of your nuget pakckage.
Also, when you install this new version of nuget package, please clean your nuget caches or delete all files under C:\Users\xxx(current user)\.nuget\packages.
When you finish the installing process, please click Build button and the files will generated under Plugins folder.
There is also a similar issue about this.

include the XML documentation from a NuGet package

I have a NuGet package with an xml documentation file (doc.xml).
I have installed the NuGet package in my project.
I know want to add the NuGet documentation file doc.xml to my solution.
I am running .net core 3.1 but I have no idea how this can be achieved.
Thank you!
If your nuget project is net standard or net core, please check these steps:
1) create a file called <package_id>.props file under a folder called build on your project.
Note that, you should make sure that the your package_id of your nuget project is the same as the .props file, otherwise, it will not work. See this link's description.
In my side, my nuget package called test.1.0.0.nupkg, so I should rename the file as test.props file.
2) please add these content in test.props file.
<Project>
<Target Name="CopyFilesToProject" BeforeTargets="Build">
<ItemGroup>
<SourceScripts Include="$(MSBuildThisFileDirectory)..\File\*.*"/>
</ItemGroup>
<Copy
SourceFiles="#(SourceScripts)"
DestinationFolder="$(ProjectDir)"
/>
</Target>
</Project>
The propose of this target is to copy the xml file from the File folder of the nupkg into the target project's folder when you install this nuget into the main project.
3) add these under xxx.csproj file:
<ItemGroup>
<None Include="bin\Debug\netcoreapp3.1\test.xml(the path of the xml file in your nuget project)" Pack="true" PackagePath="File"></None>
<None Include="build\test.props(the path of the test.props file in your nuget project)" Pack="true" PackagePath="build"></None>
</ItemGroup>
4) Then, when you pack your project, the structure should be this:
Before you install this new version of nuget package, you should clean nuget caches first or just delete all cache files under C:\Users\xxx(current user)\.nuget\packages to remove the old ones in case you still install the old one.
After that, rebuild your main project to execute the target and you will see the xml document file exist under the main project folder.
In addition, there is a similar issue you can refer to and if you use the net framework project, the link also provide the method.
===========================
Update 1
If you want to copy the file into bin\Release or bin\Debug, you should modify step 2, change to use this in the .props file:
<Copy
SourceFiles="#(SourceScripts)"
DestinationFolder="$(ProjectDir)$(OutputPath)"
/>
or just
<Copy
SourceFiles="#(SourceScripts)"
DestinationFolder="$(OutDir)"
/>
as you want.
Before you install this new version, you should first delete nuget caches under C:\Users\xxx(current user)\.nuget\packages.

Create a Nuget package without its dependencies to be added as references

I need to create a Nuget package out of my library .net framework project.
The problem is that my project depends on many other dlls which I do not want my Nuget users to see. So I want people to install my package and see only my project dll in the references list, while all its dependencies will be added to bin folder only.
Actually, this is what happens when I reference this dll in any project- I can see it in my references list, and all its dependencies are simply copied to bin folder right after building. Can this behaviour be achieved with this project as Nuget ?
Create a Nuget package without its dependencies to be added as references
Since you do not want your Nuget users to see the other dlls, so you can not set those dlls file as dependencies, which will be added to the references list by default.
To resolve this issue, we need add those dlls file in the other folder in the .nuspec file and add a function to copy this dlls file to the bin folder when we add this nuget package to the project. You can follow below steps:
Add a xx.targets file in your project folder(The one you used to create nuget package ), make sure the name of the target file is the same name as the package id(TestDemo is my package ID, so the name of .targets is TestDemo.targets).
Add below code in the targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)*.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Note: The path of "$(MSBuildThisFileDirectory)" should be relative path, if you are not familiar with it, you can use the absolute path.
In the nuspec file, add required file to the Build directory along with the targets file.
<files>
<file src="<ThoseDllsPath>\*.dll" target="Build\" />
<file src="TestDemo.targets" target="Build\" />
<file src="bin\Debug\TestDemo.dll" target="lib\462" />
</files>
Pack this package, then add it on other project to test, it work fine.
Hope this helps.
I tried the solution of Leo Liu-MSFT. It worked in a .NET Framework Project, but not in a .NET Core 3.0
If you're using packages.config to manage your library's NuGet dependencies and building the nupkg with nuget.exe pack, then I believe you'll need to generate your own nuspec file and simply not add your build-time dependencies as a dependency to your package.
If you're using PackageReference, then you can use PrivateAssets
<ItemGroup>
<!-- ... -->
<PackageReference Include="Contoso.Utility.UsefulStuff" Version="3.6.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<!-- ... -->
</ItemGroup>

NuGet only imports my .targets file into the top project

I have multiple projects in my solution, they all have the same NuGet package installed. I installed this NuGet package via the 'Manage Nuget Packages for Solution' option in my menu.
Now this particular NuGet Package has a .targets file in my build folder that imports some files into the build.
When I rebuild my solution these files are only copied for the top-level project. The other projects don't get the files copied into their build folder. This is causing me headaches because all the projects need these files in their build folder.
When I look into my .csproject files I see the following difference:
Project 1
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\NuGetTester.1.0.1\build\NuGetTester.targets" Condition="Exists('..\packages\NuGetTester.1.0.1\build\NuGetTester.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\NuGetTester.1.0.1\build\NuGetTester.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NuGetTester.1.0.1\build\NuGetTester.targets'))" />
</Target>
</Project>
Project 2
The 2nd project does not have these lines.
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
When I include these lines in the 2nd project everything seem to work.
Can someone explain why this only happens for the first project? Can I somehow force that every project in the solution that installs this NuGet will also include these lines? I can't force my users to include this manually.
Edit, this is the .targets file.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)\Versions\*.sql">
<Link>App_Data\Versions\%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
Thanks
Can someone explain why this only happens for the first project? Can I somehow force that every project in the solution that installs this NuGet will also include these lines?
Without install nuget package log, I could not give the reason that why this only happens for the first project. But you should make sure you have installed the nuget package to all projects when you installed the NuGet package via the 'Manage Nuget Packages for Solution' option:
If all check boxes are checked, you still have this issue, you should install individually that package to the problematic project, then check the install log.
Just in case, there is a workaround for this issue. You can create a MSBuild project file named "Directory.Build.props" in the same folder as your solution with you content in it.
It will be primitive imported into all projects in the directory hierarchy.
Check this document for more details.
Hope this helps.
It appears, to my knowledge, that the order of the projects was not the problem.
The problem only appears when I have no files in my target (net46) folder in the build folder.
Once I placed a random file into the .NET targetted folder the .targets file was imported into the project. Or if I removed all the .NET target folders from the build it works as usual.
So just to make a summary.
Build folder with no .NET framework target folders -> It works
Build folder with an empty .NET framework target folder -> Does not work
Build folder with a .NET framework target folder filled with one or many files -> It works

Resources