This is a series on the latest 2.* .Net Core bits, Following on from the original .Net Core Series
- Getting Started
- What’s in the box
- Using Multiple Projects
- Testing
- NuGet <=(We are here)
- Multi-targeting
- Publishing Portable Applications
- Self-contained Applications
(At the time of writing, 2.1.4. I use windows, you don’t have to!)
Let’s set aside Application packing for later, and focus our attention on NuGet.
Our Demo library
Again we will quickly whip up a library to use:
mkdir MyPackageRepo
cd MyPackageRepo
mkdir MyLib
cd MyLib
dotnet new classlib
cd ../
dotnet new sln
dotnet sln add ./MyLib/MyLib.csproj
dotnet build
Some quick touch-ups to make it more interesting. Replace .\MyLib\Class1.cs
with this again:
using System;
namespace MyLib
{
public class Calculator
{
public int Add(int first, int second)
{
return first + second;
}
}
}
At least now our package contains something interesting. One more dotnet build
to ensure our code is correct.
dotnet build
Microsoft (R) Build Engine version 15.5.180.51428 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 15.94 ms for C:\dev\MyPackageRepo\MyLib\MyLib.csproj.
MyLib -> C:\dev\MyPackageRepo\MyLib\bin\Debug\netstandard2.0\MyLib.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:01.80
Simple Packing
It isn’t very difficult to turn our library into a nupkg
(pronounced NUP-KEG, of course).
dotnet pack
Microsoft (R) Build Engine version 15.5.180.51428 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 15.86 ms for C:\dev\MyPackageRepo\MyLib\MyLib.csproj.
MyLib -> C:\dev\MyPackageRepo\MyLib\bin\Debug\netstandard2.0\MyLib.dll
Successfully created package 'C:\dev\MyPackageRepo\MyLib\bin\Debug\MyLib.1.0.0.nupkg'.
This leaves us with a directory structure like so:
MyPackageRepo
|-- nugetdemo.sln
+-- MyLib
|-- Class1.cs
|-- MyLib.csproj
+-- bin
+-- Debug
|-- MyLib.1.0.0.nupkg
+-- netstandard2.0
|-- MyLib.deps.json
|-- MyLib.dll
\-- MyLib.pdb
(Note that I have left out the obj
folder), we can see a new file has been created under /bin/Debug
, called MyLib.1.0.0.nupkg
.
This is a NuGet package (nupkg
). Pretty easy huh? The default version number comes from the *.csproj
file. (More on that soon.)
But that was a Debug
build. To produce a release build instead:
dotnet pack -c Release
Now we have more new files:
MyPackageRepo
|-- nugetdemo.sln
+-- MyLib
|-- Class1.cs
|-- MyLib.csproj
+-- bin
+-- Debug
| |-- MyLib.1.0.0.nupkg
| +-- netstandard2.0
| |-- MyLib.deps.json
| |-- MyLib.dll
| \-- MyLib.pdb
+-- Release
|-- MyLib.1.0.0.nupkg
+-- netstandard2.0
|-- MyLib.deps.json
|-- MyLib.dll
\-- MyLib.pdb
We can see that we now have a Release
folder (containing a netstandard2.0
folder, thanks to an automatic build before the pack) and in the Release
folder, we again have a MyLib.1.0.0.nupkg
file.
Crack open a nupkg
What do these nupkg
files look like on the inside? Using a tool like NuGet Package Explorer we can take a look. The Release version looks like this:
MyLib.1.0.0.nupkg
|-- MyLib.nuspec
+-- lib
+-- netstandard2.0
\-- MyLib.dll
Some things to observe. We only have a lib file for netstandard2.0
target profile. This means that our NuGet package only works with new .Net Core applications compatible with NetStandard2.0 or newer. (Remember that version compatibility table?)
Changing our attention to the contents of the nuspec
file:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>MyLib</id>
<version>1.0.0</version>
<authors>MyLib</authors>
<owners>MyLib</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Package Description</description>
<dependencies>
<group targetFramework=".NETStandard2.0" />
</dependencies>
</metadata>
</package>
We didn’t add any extra settings our *.csproj
file. But we can see here that the csproj
and project folder name MyLib
is used to populate the package id
, authors
and owners
. Let’s remind ourselves what the csproj
looks like:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
We also see that under dependencies there is a group for the targetFramework
".NETStandard2.0"
, although currently, it has no dependencies.
Custom Metadata
We looked at some of the csproj
metadata in Part 2, what’s in the box. We saw you could set the following properties:
<PropertyGroup>
<Authors>csMACnz</Authors>
<NeutralLanguage>en-NZ</NeutralLanguage>
<Company>csMACnz</Company>
<Product>MyApp</Product>
<Copyright>Copyright © csMACnz 2018</Copyright>
</PropertyGroup>
These will map metadata into our NuGet package. Let’s learn some more properties before we try generating a new nupkg
.
We have AssemblyTitle
, Title
, and Description
that out nupkg
can take advantage of (as well as other build processes that might want them, such as the dll
name). We also have some Package*
properties specifically for packages. PackageId
, PackageRequireLicenseAcceptance
, PackageLicenseUrl
, PackageIconUrl
, PackageTags
PackageVersion
and PackageReleaseNotes
.
Special mention for PackageVersion
. This is optional, and by default, the Version
property value will be used. You can explicitly add Version
, or it will implicitly be 1.0.0
, as we saw earlier on.
We can now update our csproj
to look like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Authors>dev-markc</Authors>
<NeutralLanguage>en-NZ</NeutralLanguage>
<Company>csMACnz</Company>
<Product>MyLib Product</Product>
<Copyright>Copyright © csMACnz 2018</Copyright>
<AssemblyTitle>MyLib.Title</AssemblyTitle>
<Title>MyLib - Cool Stuff Here</Title>
<Description>MyLib - A library that does cool things with stuff.</Description>
<PackageId>MyLib.PackageId</PackageId>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageLicenseUrl>https://myprojectwebsite.uri/projects/MyLib/LICENSE</PackageLicenseUrl>
<PackageProjectUrl>https://myprojectwebsite.uri/projects/MyLib</PackageProjectUrl>
<PackageIconUrl>https://myprojectwebsite.uri/icons/mylib.png</PackageIconUrl>
<PackageTags>sample test lib stuff things</PackageTags>
<PackageVersion>1.2.3</PackageVersion>
<PackageReleaseNotes>These are sample release notes for this version</PackageReleaseNotes>
</PropertyGroup>
</Project>
And again we pack, and review the nuspec
file inside the generated nupkg
.
dotnet pack -c Release
This time, we have a MyLib.PackageId.1.2.3.nupkg
file, which is because we used PackageId
to get MyLib.PackageId
and PackageVersion
to get the 1.2.3
.
Inside the MyLib.PackageId.1.2.3.nupkg
there is a MyLib.PackageId.nuspec
which looks like the followiing:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>MyLib.PackageId</id>
<version>1.2.3</version>
<title>MyLib - Cool Stuff Here</title>
<authors>dev-markc</authors>
<owners>dev-markc</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<licenseUrl>https://myprojectwebsite.uri/projects/MyLib/LICENSE</licenseUrl>
<projectUrl>https://myprojectwebsite.uri/projects/MyLib</projectUrl>
<iconUrl>https://myprojectwebsite.uri/icons/mylib.png</iconUrl>
<description>MyLib - A library that does cool things with stuff.</description>
<releaseNotes>These are sample release notes for this version</releaseNotes>
<copyright>Copyright © csMACnz 2018</copyright>
<tags>sample test lib stuff things</tags>
<dependencies>
<group targetFramework=".NETStandard2.0" />
</dependencies>
</metadata>
</package>
Most mappings from csproj
to nuspec
are self-explanatory. Also, some are not used here (Product
, Company
, AssemblyTitle
) but you will find these as metadata on the dll
instead.
I haven’t been completely comprehensive here. The full documentation of the NuGet metadata properties is in the docs.
And the next time…
We can build packages to be used by new .Net Core applications and NetStandard2.0
compatible application. But we probably want to build libraries that can be used from older .Net 4.0, Windows Phone, older UWP applications and .Net Core 1.0 apps as well. Next up, we see how to extend our package to handle just these situations.