Creating Nuget Packages from .Net SDK style projects (.net core, etc)

When you use project files with the new .Net SDK project specification, you can easily expose those projects as nuget packages, via msbuild or the dotnet pack command. This is useful, as you don’t need to use nuget.exe, and you do not need to author and maintain a nuspec file.

Nuget.exe does not fully understand the new SDK project format, and also does not understand multi-targetting, so you might not have Nuget.exe as an option.

GeneratePackageOnBuild

To get your project to build a nuget package, you can set the GeneratePackageOnBuild element in your project file to true.

  <PropertyGroup>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

This is nice and simple, but it can slow down your build process, as it generates a package on each build, even when you don’t need a package generating.

IsPackable

You can specify whether a project is packable. This defaults to true, so if you don’t specify IsPackable, and run dotnet pack, every project will get packed, even the ones you don’t want to pack!

  <PropertyGroup>
    <IsPackable>true</IsPackable>
  </PropertyGroup>

By using IsPackable, you can split build and pack into two individual steps, and have more finely grained control over your package creation.

Including other assets in your packages

You can include assets from your project by adding them as an item in your project file.

  <ItemGroup>
    <None Include="AppIcon.ico" Pack="true" PackagePath="\" />
  </ItemGroup>

You can even get fancy and include whole folders by using wildcards. A single wildcard will take everything in the path you’ve specified, whereas a double wildcard will recursively include everything including child folders.

  <ItemGroup>
    <None Include="images\**" Pack="true" PackagePath="\" />
  </ItemGroup>

For more information on using wildcards, refer to the msdn documentation.

Making a self contained package

If you want to expose an application as a nuget package, you may need to directly include all dependencies in your nuget package. This is required if you wish to make the package available over chocolatey.

⚠️To achieve this, we need to work around a few quirks. Project items can only refer to items that exist before the build is executed, but what if we want to pack items that were produced as part of a build?

The answer is to build and pack as two separate steps. By doing this, all of the build artefacts have been created, and can be referenced before we execute the pack step.

Including build artefacts in a package

If you’re authoring a package that only targets a single version of the .Net Framework, you can achieve this using the following code:

  <ItemGroup>
    <None Include="bin\$(Configuration)\$(TargetFramework)\**" Visible="false">
      <PackagePath>lib\$(TargetFramework)</PackagePath>
      <Pack>true</Pack>
      <InProject>false</InProject>
      <Visible>false</Visible>
    </None>
  </ItemGroup>

Here we include everything from the current build configuration and target framework folders in the package, under the appropriate lib framework directory.

If your project targets multiple framework versions, the $(targetFramework) variable will not be defined. The code needs to be slightly modified.

  <ItemGroup>
    <None Include="bin\$(Configuration)\**" Visible="false">
      <PackagePath>lib</PackagePath>
      <Pack>true</Pack>
      <InProject>false</InProject>
      <Visible>false</Visible>
    </None>
  </ItemGroup>

Here we include everything from the current build configuration folder, and rely on the fact that each framework build will output to the correctly named framework folder. We then package these under lib.

Don’t forget to remove your dependencies

Now we’ve included all of our dependencies directly in our package, we need to tell the packer that we don’t need any external dependencies. You can do this by setting the PrivateAssets attribute on your package references to “All”.

<ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.2" PrivateAssets="All" />
    <PackageReference Include="SimpleInjector" Version="4.9.0" PrivateAssets="All" />
</ItemGroup>

You also need to do this with your project references.

  <ItemGroup>
    <ProjectReference Include="..\Project1\Project1.csproj" PrivateAssets="All" />
    <ProjectReference Include="..\Project2\Project2.csproj" PrivateAssets="All" />
  </ItemGroup>