This is a series on the latest 2.* .Net Core bits, Following on from the original .Net Core Series

(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.