Build a nuget package for .net framework executable with file dependencies included

I recently was in a situation where I had to build a nuget package for an executable application. There are 2 options:

  • The manual approach (described below)
  • The automated approach. Try calling nuget pack with the -IncludeReferencedProjects switch (see here). Maybe this will accomplish what you are looking for

Description of a manual approach to packaging the files

Unfortunately, the automated approach didn't quite work for us because there were other files beyond those in the bin directory that we wanted to pack.

Therfore, firstly I attempted to build the nuget package by issuing the following statement in a Windows command line for a .net framework application:

nuget pack

This command was issued in the same folder that the csproj file for the application source code was located. A package was built. it looked like this (viewed with the Nuget Explorer):

Package files

Users tried to open the application when deployed with Octopus to their machines. The application errored out as it didn't have the required dependencies.

In order to get round this problem, I thought I needed a nuspec file. So one was created for the application. It looked like this:

<?xml version="1.0"?> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <id>$id$</id> <version>$version$</version> <title>$title$</title> <authors>Company</authors> <owners>$author$</owners> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>Application</description> <releaseNotes>No release notes at this time</releaseNotes> <copyright>Copyright © 2018</copyright> </metadata> </package>

We then ran the nuget pack command again.

Same result. A nuget package was produced, with a single file.

So, I discovered I had to add file tags to the nuspec file. This was the file as it then looked.

<?xml version="1.0"?> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <id>$id$</id> <version>$version$</version> <title>$title$</title> <authors>Company</authors> <owners>$author$</owners> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>Application</description> <releaseNotes>No release notes at this time</releaseNotes> <copyright>Copyright © 2018</copyright> </metadata> <files> <file src=".\bin\$configuration$\*.*" target="lib\net462" /> </files> </package>

This worked, but nuget now issued a warning during the pack.

WARNING: File 'D:\App\company.Apps.Sys.App\bin\Debug\company.app.exe' is not added because the package already contains file 'lib\net462\company.app.exe'

This was because an attempt was being made to add the executable file twice to the package by nuget. By default, if no parameters are specified for a csproj that produced an executable output, the executable is added by default. So the file tags in the nuspec file required exclusion clause to ensure the executable file was only added once.

<?xml version="1.0"?> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <id>$id$</id> <version>$version$</version> <title>$title$</title> <authors>Company</authors> <owners>$author$</owners> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>Application</description> <releaseNotes>No release notes at this time</releaseNotes> <copyright>Copyright © 2018</copyright> </metadata> <files> <file src=".\bin\$configuration$\*.*" target="lib\net462" exclude=".\bin\$configuration$\*.exe"/> <file src=".\config\*.*" target="lib\config"/> </files> </package>

The second file tag included some custom config we wanted to include in our package.

The final piece of the puzzle was I wanted the build configuration to be used to select the right artifacts folder when packaging the files using the nuget replacement-tokens syntax. I found a stackoverflow post that described the $configuration$ syntax. This required that when nuget pack was use, the correct configuration options should be passed. The correct syntax is:

nuget pack -Prop Configuration=Debug

for example for the Debug configuration.

These steps ensured a nuget package was created for my executable with all dependencies and no warning.

A couple of other caveats

Make sure token are hardcoded with values in the nuspec file or passed into the nuget pack command. For example:

$id$

should be your package id:

MyCompany.MyApp

Otherwise errors like this are raised:

2018-06-05T10:17:16.3705097Z Id is required.
2018-06-05T10:17:16.3705282Z NuGet Version: 4.3.0.4406
2018-06-05T10:17:16.3705744Z Attempting to build package from 'Apps.MyApplication.nuspec'.
2018-06-05T10:17:16.3709702Z System.Exception: Id is required.
2018-06-05T10:17:16.3792246Z at NuGet.Packaging.Manifest.Validate(Manifest manifest)
2018-06-05T10:17:16.3797461Z at NuGet.Packaging.Manifest.ReadFrom(Stream stream, Func2 propertyProvider, Boolean validateSchema)
2018-06-05T10:17:16.3808343Z at NuGet.Packaging.PackageBuilder.ReadManifest(Stream stream, String basePath, Func
2 propertyProvider)
2018-06-05T10:17:16.3809164Z at NuGet.Packaging.PackageBuilder..ctor(String path, String basePath, Func`2 propertyProvider, Boolean includeEmptyDirectories)
2018-06-05T10:17:16.3809417Z at NuGet.Commands.PackCommandRunner.CreatePackageBuilderFromNuspec(String path)
2018-06-05T10:17:16.3809619Z at NuGet.Commands.PackCommandRunner.BuildFromNuspec(String path)
2018-06-05T10:17:16.3809778Z at NuGet.CommandLine.PackCommand.ExecuteCommand()
2018-06-05T10:17:16.3810058Z at NuGet.CommandLine.Command.ExecuteCommandAsync()
2018-06-05T10:17:16.3810251Z at NuGet.CommandLine.Command.Execute()
2018-06-05T10:17:16.3810581Z at NuGet.CommandLine.Program.MainCore(String workingDirectory, String[] args)
2018-06-05T10:17:16.4575955Z ##[error]The nuget command failed with exit code(1) and error(Id is required.)

Secondly, the exclusion of executables step above is optional. Leave it out

exclude=".\bin\$configuration$*.exe"

if required.

Thanks to these posts on Stackoverflow

PS there may be questions as to why we didn't use Octopack for this process. We couldn't as we had other requirements which meant Octopack was no appropriate to use.