Recently, I started to pack nuget packages out of my several projects. First I started with the Package Explorer application. It is a nice tool, but it's less useful if you do continuous integration. Then I looked into specifying the nuspec template file, and passing changing data, e.g. version number, as command line arguments. Later, I wondered how to define the nuget package dependencies. As it turns out, the nuget.exe already does this based on the package.config if you specify a csproj. Moreover, it extracts relevant data like Author, Version, Copyright right from the assembly info. What I'm missing right now is the ability to specify a licenseUrl in the command line. But I wanted the question to be more generic. And so I'm asking:
What is the prefered way to pack nuget packages?
Here's a little-known fact: you can combine both!
Target a csproj file, and make sure there's a nuspec file in the same directory with the same name as the csproj file. NuGet will merge the two during package creation.
So in short: target <ProjectName>.csproj, optionally add a corresponding tokenized <ProjectName>.nuspec file to be used as metadata by NuGet.exe.
It saves you from managing output location, dependencies, version, and other stuff that can be derived from the project.
With a .csproj for Visual Studio 2017, you don't need a .nuspec file. You can actually add the values directly to your csproj and it will pick them up.
Right click the project in Visual Studio, Edit xxxxx.csproj. Notepad works fine too.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.1</Version>
<authors>Subtracts</authors>
<TargetFrameworks>netstandard1.6;net452</TargetFrameworks>
<AssemblyName>Checkout.net</AssemblyName>
<PackageId>Checkout.net</PackageId>
...
</Project>
p.s. Since I don't have sufficient reputation to comment, I am leaving an answer instead of a comment on Xavier's answer. :)
For simple packages you can directly create the packages off .csproj or .vbproj. But for more advance packages, especially when you need to pull in custom files into your package, you need to use .nuspec. I usually start off with the csproj and move to nuspec as needed. You can always get the nuspec using the command nuget spec on the csproj.
https://docs.nuget.org/create/creating-and-publishing-a-package
You can specify any of the properties including licenseUrl using the Properties parameter to nuget pack
nuget pack -properties licenseUrl=http://blah
With .NET Core as of February 2018 you'll need to supply a .nuspec file for any more than the basic spec file properties.
But the dotnet pack command will not use the .nuspec file unless you add <NuspecFile>relative path to nuspec</NuspecFile> to the .csproj file.
See https://github.com/dotnet/cli/issues/2170
Most packages can now be made without a .nuspec file. The thing to watch is the dependencies. You may need to add a PrivateAssets element to some that are tools, like msbump and um, SpecFlow maybe.
<PackageReference Include="msbump" Version="2.3.2">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
This stops this package dependency "flowing" to the dependencies of your package.
Also worth reading about specifying versions in the most flexible way.
https://learn.microsoft.com/en-us/nuget/consume-packages/dependency-resolution#floating-versions
And range syntax.
https://learn.microsoft.com/en-us/nuget/reference/package-versioning#references-in-project-files-packagereference
Related
I need to use a NuGet package containing a utility for my project. It contains several binaries (EXEs and DLLs).
I've added it to my project successfully but I suspect the nupkg isn't formed correctly because I cannot use any of its DLLs or EXEs in my project without manually pointing to the package in my local NuGet cache. When compiling, none of its resources are added to the output (I assume this is because nothing is referenced in my code).
I'd like to create a wrapper project to call the binaries but I'd also like other project devs to be able to compile the solution without adjusting directory variables. Ideally, I could configure the csproj to pull in the bits directly from the local package cache. I think this would be possible by setting the Generate Path Property value to Yes in Visual Studio, but the variable cannot be found when I attempt to use an <Include/> statement in the csproj file.
Is what I'm asking possible? Namely, reference the NuGet package bits within my csproj to ensure the binaries are dropped in the compilation output? Can I do this with the Path Property, or is there something else I can do without directly committing the package's binaries into my project?
(I realize I need to work with the developer to fix whatever issue they have with their package, but I have no direct influence at the moment so this is the best I can do at the moment).
I figured this out, mostly due to misunderstanding how some of the different tags and attributes are meant to be used.
To achieve the desired effect, I did the following:
<ItemGroup>
<Content Include="$(Pkg{PackageId})\**">
<Link>{NameOfSolutionDirectory}\%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
Where {PackageId} is the name of the NuGet package (this step requires setting 'Generate Path Property' to 'Yes' in the package properties via Solution Explorer), and {NameOfSolutionDirectory} is the name of a folder within the solution I'd like to use for containing those bits, if you're as concerned about keeping the project as organized as I am. The {} should be excluded when replacing these values.
If you want to scope to a specific directory within the package contents, do it within the Include attribute. The ** is necessary if you want to include all files within that directory, or else you can scope by extension or whatever additional pattern you'd like.
I have added .props file and included it in .nuspec file.
In my .props file i have added new properties/elements in PropertyGroup.
After installing my nuget package, .props file is importing successfully in .csproj file. But how can I include/add props files properties/variables in HintPath (not manually).
Using HintPath & .nuspec for your nugets is an indicator of legacy approach, consider PackageReference & SDK style csproj instead, there is no hint path at all, they are all automatic.
Legacy of Hint Path
Hint path usually used as an item metadata for Reference items for nuget-installed assemblies. Legacy project format been developed when nuget was an external tool (now it is part of .Net SDK). At that time the approach was to have packages.config (you no longer need it) with the list of nugets installed to the project, and while package been installed it have to appear to the rest of .net tools as a regular sideloaded set of assemblies, this is why DLL files are been restored to "packages" folder (this no longer happens), and Reference items are been added to the project with a HintPath having relative path to this "packages" folder. This includes whole graph of references, so if PackageA uses PackageB then both are deployed to "packages" folder and both produces Reference item on all libraries from every package. This way legacy tools used to bring package management to existing ecosystem. For a new PacakgeReference approach you only have to specify top-level package, all transient references will be processed automatically on a restore & build time.
Custom Targets
Since you are mentioning you have props file in your nuspec, let me clarify how now it can be achievend in SDK style csproj in a package that your are publishing (there is no nuspec, all nuspec data is now inside csproj):
<ItemGroup>
<None Include="your_custom.props_or_targets">
<Pack>true</Pack>
<PackagePath>build</PackagePath>
</None>
</ItemGroup>
So, destination path is specified in PackagePath item metadata and it should be build to denote that this is auto-installable targets.
Now in legacy approach when installing package like this, a consumer project automatically have "Import" node to explicitly bring this targets to the project. Now in new SDK approach, if you import package like this with PackageReference, you will not find Import node - it is actually implicitly assumed, so if package under question have targets & props in build folder - all of them will be automatically imported in a build time (not in design time).
PackageReference
<ItemGroup>
<PackageReference Include="YourPackage" Version="3.6.0" />
</ItemGroup>
So if you consume project like this, this is enough to consume Properties that you bring with your custom props inside this package. Feel free to use them as usual an hopefully you no longer need HintPath at all.
I wrote a class library in C# that I need to push to a private NuGet server (v3.4.1.0). I decorated my classes and methods with XML documentation comments.
XML documentation file option is checked on the Build tab of the project properties panel, the project builds successfully and the xml file gets generated on the project's root folder with the same name as the assembly.
In the .csproj file the related section looks like this:
<PropertyGroup>
<DocumentationFile>absolutePathTo\assemblyName.xml</DocumentationFile>
</PropertyGroup>
IntelliSense (VS2019 16.9.0) recognises the documentation and shows it properly even in other projects under the same solution.
When I generate the NuGet package it gets created in the project's bin\Debug folder. If I open it as a zip archive the DLL and the documentation XML can be found in the lib\netstandard2.1 folder having matching names.
Once I install this package to another project from the private NuGet server it works properly but loses the complete documentation. IntelliSense does not show my comments anymore and the assembly metadata seems not to have it either.
Could anyone support me on this one?
That is normal. For xml document, it is special under new-sdk style projects. The xml document could only be copied into the non-sdk net framework projects but new-sdk net core projects cannot. More similar to this issue I handled before.
So you should try these steps additionally to get what you want:
1) enter these node under csproj file of your nuget project.
<ItemGroup>
<None Include="xxx\absolutePathTo\assemblyName.xml"(the path of the xml file under your project folder) Pack="true">
<PackageCopyToOutput>true</PackageCopyToOutput>
</None>
</ItemGroup>
2) after that, re-pack your nuget project and before you install the new version, please clean nuget caches first or just delete all files under C:\Users\xxx\.nuget\packages
Personally, I let Visual Studio handle things for me.
If you right-click in the project, and choose Properties,
In the Build -> Output area, you should see a checkbox under Documentation file labelled Generate a file containing API documentation..
When you check this, a new option appears underneath: XML documentation file path. But the file selector is labelled Optional path for the API documentation file. Leave blank to use the default location..
FYI: The default location is alongside your EXE / DLL that is generated when you build your project.
When you next build your code (for anyone else reading, I assume you've got the Generate NuGet package on build in the Package area checked too) it will also package up the XML documentation into the NuGet package generated.
From the perspective of users of this new package, Visual Studio will pick up on the XML Documentation inside.
I need to create csproj file that will be usable as project reference in VS2013 and will output prebuilt binary as it's "Build" result.
We use referenced projects for build, however company policy doesn't allow access to some of that projects for everyone. As a result projects need to be updated manually to make them build. This is really a major inconvenience when switching branches and when making edits to project files, so I want to create dummy project that will be bound to pre-built binaries as their "output" and will be placed instead of real projects.
EDIT: Moving that assembly to Nuget package is not an option for now since Nuget has some issues with dev flow (when you need to debug/test/develop package). I saw some VS extension that implements switching between Nuget package and local project which might solve this issue, but I'm not sure if it will be accepted and want to explore other options.
To be clear - the thing I want to avoid is editing project in any way, so that project can be built cleanly after pulling it from Git, and I don't have to clean it every time before commit.
I haven't properly tested it, but the solution seems really simple (if I understand the question properly).
Just add this to the existing .csproj, overriding the Build target to just give the path to the pre-built assembly.
<Target
Name="Build"
Returns="$(TargetPath)" />
This assumes the TargetPath property already defined, and it should automatically be if you're modifying the original .csproj. Otherwise just define it yourself in a <PropertyGroup> before the Build task.
Note that having TargetPath defined is important for the ProjectReferences in your own project to resolve.
How about having those restricted (binary only) projects reside in an internal Nuget package feed, so that Nuget can install the packages as needed, on build?
Intro (how to pack resources into a nuget package)
To pack some resource files into a nuget package, what one would normally do, is the following.
Put all the resource files into the content\ directory of a nuget package. This would be specified by the following line in a .nuspec file:
<files>
<file src="Project\bin\Release\script.js" target="content\js\script.js" />
<files>
Now, when this nuget package gets installed into AnotherProject, the following file structure emerges:
Solution.sln
packages\Project.1.0.0\content\js\script.js // the original resource file
AnotherProject\js\script.js // a physical copy
AnotherProject\AnotherProject.csproj // <Content /> tag (see below)
During package installation, AnotherProject.csproj was injected with tag:
<Content Include="js\script.js" />
and this is for the physical copy of the original resource (which is under packages\ directory).
The actual problem (how to pack resources into a nuget package as link)
My aim is not to have the physical copy of a resource file in the AnotherProject directory but rather a "link" to the original resource under packages\ directory. In the csproj, this should look like this:
<Content Include="packages\Project.1.0.0\content\js\script.js">
<Link>js\script.js</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Brute force solution that I would rather avoid
Now, one "do it the hard way" workaround I can think of is:
not putting resource files under content\ so they do not get added automatically,
writing Install.ps1 script that would hack the csproj file structure and add the necessary XML piece manually,
This, however, has the following drawbacks:
all my nuget packages need the same script piece in their Install.ps1,
when installing my packages, there would be a nasty "project reload prompt" in Visual Studio.
Since NuGet currently does not support this out of the box your options are either to use PowerShell or to use a custom MSBuild target.
PowerShell
Leave your resources outside of the Content directory in your NuGet package (as you already suggested).
Add the file link using PowerShell in the install.ps1.
You should be able to avoid the project reload prompt if you use the Visual Studio object model (EnvDTE). I would take a look at Project.ProjectItems.AddFromFile(...) to see if that works for you.
MSBuild target
NuGet supports adding an import statement into a project that points to an MSBuild .props and/or .targets file. So you could put your resources into the tools directory of your NuGet package and reference them from a custom MSBuild .props/.targets file.
Typically the custom .props and .targets are used to customise the build process. However they are just MSBuild project files so you could add items for your resources into these project files.
Note that .props are imported at the start of the project file when a NuGet package is installed, whilst .targets are imported at the end of the project.
Customising NuGet
Another option, which would take more work, would be to modify NuGet to support what you want to do.