Coding

NuGet like a Pro, the MSBuild way

Back in 2012 I posted an article on this blog called “Creating Packages with NuGet the MSBuild Way“. That post described an MSBuild-integrated method to create NuGet packages from your own source code. It has remained one of my most popular posts. Like many popular things on the internet, it has been out of date for sometime now. When I check on my blog and see that the most visited article of the day is “Creating Packages with NuGet the MSBuild Way“, I wonder if visitors know that its out of date. Do they dismiss me as a crank and leave the page immediately? Even worse: do they follow the outdated and complicated recipe described in the post?

In 2012, I needed to integrate packaging into MSBuild because I could not find a plug-in for CruiseControl.net that would create NuGet packages.  There may be a plug-in now, I don’t know.  After a couple years creating NuGet packages, many tools I use from day to day have changed including my source control and continuous integration options. Even though I now have the option to create CI builds on TFS, where NuGetter is available, I still use MSBuild to configure my projects to create packages every time I hit F6.

I have a new, simple process for setting this up and it usually takes me about five minutes to convert an existing project to produce a NuGet package as part of it’s build output. I start with some existing code, enable package restore, make one small edit to my project file, and build. That’s all it takes to get the first package in the first five minutes.

If I want to continue customizing after creating the first package, I pull the nuspec file out of the existing package, put the nuspec file next to my project file, and customize from there, that’s the second five minutes.

Finally, I make some small modifications to the nuget.targets file provided by package restore in order to automate some cleanup, that takes about five more minutes.

It takes me about fifteen minutes to get everything setup just how I like it, but if your needs are simple, you can be done in five minutes. Hopefully this simplified process will be much more useful to my future visitors and help you, dear reader, understand how easy it is to create NuGet packages for your open source (or private) packages.  So read on for all the details!

Build

Start with Some Code

Any Class Library will do.  The important thing is that its something you want to share.  Either its your open source project, or a bit of private code which you’d like to share with your customers, other departments in your organization, or just your team.

For this example I’ve created a super-cool class called TemporaryFile.  TemporaryFile provides a disposable wrapper around a FileInfo which deletes the file when the Dispose method executes.  This allows the user to control the lifetime of the temporary file with a using statement, or trust the garbage collector to take care of it during finalization.  I find myself creating and deleting temporary files for a certain class of unit tests, and a wrapper like this takes alot of the grunt work out of the task.

namespace TemporaryFile
{
    using System;
    using System.IO;
    using ApprovalUtilities.Utilities;

    public class Temp : IDisposable
    {
        private readonly FileInfo backingFile;

        public Temp(string name)
        {
            this.backingFile =
                            new FileInfo(PathUtilities.GetAdjacentFile(name));
            this.backingFile.Create().Close();
        }

        ~Temp()
        {
            this.Dispose();
        }

        public FileInfo File
        {
            get
            {
                return this.backingFile;
            }
        }

        public void Dispose()
        {
            // File on the file system is not a managed resource
            if (this.backingFile.Exists)
            {
                this.backingFile.Delete();
            }
        }
    }
}

Notice that the class uses a method from PathUtilities in ApprovalUtilities (part of ApprovalTests).  I added this method call solely to generate a dependency on another package, which in turn helps demonstrate how much metadata NuGet can infer for you without explicit configuration.  Relying on inference is a big part of keeping this process fast an simple–as long as the inferred information meets your needs.

However, the way I used PathUtilities here turned out to be a bug.  So don’t copy this code.  It is useful to have a bug in the code when doing demos, so I left it in there.  If you think the temporary file idea sounds super useful, then a bug free version is now available as part of ApprovalUtilities.

If you examine the NugetLikeAPro repository on GitHub, TemporaryFile is a plain old .net # class library.  It has a test project but not much else is going on.

Enable Package Restore

The NuGet documentation is very good, and covers a lot of ground but if it covered everything then you wouldn’t need me!  I think that “Using NuGet without committing packages to source control” contains a lot of good information about what happens when you click the “Enable Package Restore” menu item, but it does not emphasize something very important to us as package creators: the NuGet.Build package installed by package restore contains everything you need to convert a project to create packages.

When you enable package restore, two packages are added to your solution: NuGet.CommandLine and NuGet.Build.  You could add these yourself, but that would be two steps instead of one.  Package restore also performs a third, more tedious step for you: it updates your project files to reference a new MSBuild script and adds a $(SolutionDir) property so that the new script can do its work.  The project files need to reference an MSBuild script (NuGet.targets) in order to run the package restore target before the build.  The package restore article doesn’t mention that the script also defines a build package target, which can create a package for you after the build completes.

So, lets enable package restore on TemoraryFile and see what we get.

Image of the Visual Studio solution context menu
Enable Package Restore

Just as promised by the documentation, the process added a solution folder and three files: NuGet.targets, NuGet.exe, and NuGet.Config.  NuGet.Config is only needed by TFS users so you can probably delete it safely.  It has no impact on what we are doing here.  By observing red checkmarks in the Solution Explorer we can also see that the process modified TemporaryFile.csproj and TemporaryFile.Tests.csproj.

Image showing Visual Studio solution explorer
Modifications to Solution

Lets see what changes package restore made to TemporaryFile.

diff --git a/NugetLikeAPro/TemporaryFile/TemporaryFile.csproj b/NugetLikeAPro/TemporaryFile/TemporaryFile.csproj
index c1e5a2c..85e156b 100644
--- a/NugetLikeAPro/TemporaryFile/TemporaryFile.csproj
+++ b/NugetLikeAPro/TemporaryFile/TemporaryFile.csproj
@@ -11,6 +11,8 @@
 <AssemblyName>TemporaryFile</AssemblyName>
 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
 <FileAlignment>512</FileAlignment>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
+ <RestorePackages>true</RestorePackages>
 </PropertyGroup>
 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
 <DebugSymbols>true</DebugSymbols>
@@ -49,6 +51,13 @@
 <None Include="packages.config" />
 </ItemGroup>
 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
 Other similar extension points exist, see Microsoft.Common.targets.
 <Target Name="BeforeBuild">

Lines 18-24 create the reference to the NuGet.targets file in the .nuget folder, and add some error handling if the script is missing during the build.  On line 9 the $(SolutionDir) property is created, and its default value is the project’s parent directory.  NuGet.targets uses this piece of configuration to find resources it needs, like NuGet.exe or the solution packages folder.  Finally on line 10, package restore is enabled by adding the RestorePackages property and setting it’s value to true.  (Side note: this is a bit misleading.  It is getting harder and harder to opt-out of package restore.  If you set this to false, Visual Studio will set it to true again during the build, unless you opt-out again using a separate Visual Studio option.)

Editing project files is a bit tedious because you have to unload them, open them again as XML files, make your changes and then reload them.  Its not hard to learn but its at least four mouse clicks and then some typing in an obscure syntax without much intellisense (although R# helps here).  It’s nice that the Enable Package Restore menu item did all that editing for you with one click.  Remember that the process also added two NuGet packages for you, so you can add all that to your overall click-savings.  Note that the documentation mentions a new feature available in NuGet 2.7 called “Automatic Package Restore“.  This feature is enabled by default and solves some problems caused by package restore in certain scenarios.  It’s already on by default, so we can imagine that someday a program manager at Microsoft is going to say, “Hey, lets get rid of that ‘Enable Package Restore’ menu item.”

If the Enable Package Restore “gesture” is ever removed then we can install the NuGet packages ourselves and make the necessary changes to the project files.  This will get tedious and use way more than the five minutes I’ve allotted to the process, so I’m sure someone will think of a clever way to automate it again with yet another NuGet package.  However, this is all just my own speculation.  Today we live in the Golden Age of NuGet package creation, and package restore does 99% of the work for us.

One Small Edit

The NuGet.targets file provided by the NuGet.build package provides a “BuildPackage” target.  Unlike the “RestorePackages” target, the build package target is not enabled by default.  So, we have to edit our project file to turn it on.  To edit the file in Visual Studio is a several step process.  If I were to make the change from within the IDE, I would: right-click on the  TemporaryFile node in Solution Explorer, select “Unload Project”, right click again, select “Edit Project”, edit the project file, save the project file, close the project file, right-click the project again, select “Reload Project”.  It’s a hassle.

An image of the project context menu in Solution Explorer
Too Much Work

I find it’s easiest to use a regular text editor to make this change rather than Visual Studio.  Anything should work, I often use Sublime Text or Notepad++.  Plain old notepad or WordPad should work fine.  I prefer Sublime because I keep a my “Projects” folder open in Sublime by default so that I can glance at code or edit these types of files quickly.  However you choose to do it, you only need to add one property in order to turn on the BuildPackage target.

diff --git a/NugetLikeAPro/TemporaryFile/TemporaryFile.csproj b/NugetLikeAPro/TemporaryFile/TemporaryFile.csproj
index 85e156b..e42d010 100644
--- a/NugetLikeAPro/TemporaryFile/TemporaryFile.csproj
+++ b/NugetLikeAPro/TemporaryFile/TemporaryFile.csproj
@@ -13,6 +13,7 @@
 <FileAlignment>512</FileAlignment>
 <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
 <RestorePackages>true</RestorePackages>
+ <BuildPackage>true</BuildPackage>
 </PropertyGroup>
 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
 <DebugSymbols>true</DebugSymbols>

I usually put it right below the RestorePackages property (line 9), but you can choose where it goes.  For example, if you wanted to only create packages for debug builds, you could down a few lines to line 12, into the next PropertyGroup, which is only defined when Debug is selected.  The same technique would work to restrict package creation to Release builds, if that’s what you would like to do.  If you made the change out side Visual Studio, the IDE will notice and ask you if you want to reload the project.  You do, so click “Reload” or “Reload All”.

An Image of the "File Modification Detected" dialog
You need to reload now

Once the BuildPackage property is set to true, MSBuild will execute the corresponding target in NuGet.targets and create a package for you on every build.  This package will get most of it’s configuration by inference, and appear in the bin directory next to your normal build outputs.

An image of Windows File Explorer
BuildPackage creates two packages by default

BuildPackage created two packages for me.  One is an ordinary NuGet package, which contains the TemporaryFile assembly and one is a “Symbol” package, which includes the same assembly along with additional debugging resources.

An image of the standard NuGet package, open in NuGet Package Explorer
The ‘Standard’ NuGet package

We didn’t provide NuGet with any configuration information.  NuGet configured these packages by convention, and used the project and assembly information to infer what the package configuration should be.  By opening the standard package in NuGet Package Explorer we can see what NuGet came up with.  The Id, Version, Title, and Copyright are all inferred by examining assembly attributes.  These attributes are defined in AssemblyInfo.cs by default.

using System.Reflection;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("TemporaryFile")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TemporaryFile")]
[assembly: AssemblyCopyright("Copyright ©  2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("4365a184-3046-4e59-ba28-0eeaaa41e795")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Authors and Owners are both set to “James” which is my user name on the machine where I created the package. NuGet would prefer to use the value from “AssemblyCompany” for these fields, but I haven’t filled it out yet. Since AssemblyCompany was empty, NuGet moved on to the next convention and chose my user name instead. NuGet would also prefer to use “AssemblyDescription” to populate the Description value, but this was also blank. Since there is no other logical place (yet) for NuGet to find a description, the program simply gave up and used the word “Description” instead. NuGet uses the build log to warn me (lines 4, 5, 11, and 12 below) when this happens.

1>  Attempting to build package from 'TemporaryFile.csproj'.
1>  Packing files from 'C:\Users\James\Documents\GitHub\Blogs\NugetLikeAPro\TemporaryFile\bin\Debug'.
1>  Found packages.config. Using packages listed as dependencies
1>EXEC : warning : Description was not specified. Using 'Description'.
1>EXEC : warning : Author was not specified. Using 'James'.
1>  Successfully created package 'C:\Users\James\Documents\GitHub\Blogs\NugetLikeAPro\TemporaryFile\bin\Debug\TemporaryFile.1.0.0.0.nupkg'.
1>
1>  Attempting to build symbols package for 'TemporaryFile.csproj'.
1>  Packing files from 'C:\Users\James\Documents\GitHub\Blogs\NugetLikeAPro\TemporaryFile\bin\Debug'.
1>  Found packages.config. Using packages listed as dependencies
1>EXEC : warning : Description was not specified. Using 'Description'.
1>EXEC : warning : Author was not specified. Using 'James'.
1>  Successfully created package 'C:\Users\James\Documents\GitHub\Blogs\NugetLikeAPro\TemporaryFile\bin\Debug\TemporaryFile.1.0.0.0.symbols.nupkg'.

Notice on lines 3 and 10 that NuGet noticed that my project depends on another NuGet package. It infers this by detecting the ‘packages.config’ file where NuGet lists the project dependencies, reads that file, and automatically configures TemporaryFile to depend on ApprovalUtilities.

Overall NuGet did a pretty good job, and this package is actually usable.  Before we move on to customizing this package lets take a look at it’s sibling, the symbol package.

An Image of the Symbol package open in NuGet Package Explorer
The Symbol Package

The symbol package configuration is identical to the standard package.  Version, Id, Authors, and the rest are all the same.  However, there are more files in the symbol package.  Along with the class library, the lib/net45 folder contains the debugging symbols.  There is also a new folder called src.  Under the src directory, we can find all the source code for TemporaryFile.dll.  All together, this extra content gives Visual Studio enough information to provide a complete step-through debugging experience for this NuGet package.  What to do with this package and how to configure Visual Studio to use it are topics better handled on their own, so I wont cover them further here.  Stay tuned.

Customize

There are a few things I would like to change in this package before sharing it with the team/customers/world. I don’t like the default values for Author/Owner and Description. At a minimum the Author field should contain my last name, or perhaps my twitter handle or something I’d like the world to know me by. It is also appropriate to use your company name in this field. The description is important because this package will probably end up in a gallery and certainly be presented in the NuGet Package Manager inside Visual Studio. You need a good concise description so people have an idea what you are trying to share with them.  The copyright isn’t claimed by anyone either, be careful here because some default Visual Studio installs automatically use “Microsoft” as the default copy right holder (this seems to have been fixed in 2013, now its just blank).  Finally, I don’t like the default 3-dot version number, I prefer the 2-dot version, so I’d like to change that too.  These are the low hanging fruit which can be customized using AssemblyInfo.cs.

diff --git a/NugetLikeAPro/TemporaryFile/Properties/AssemblyInfo.cs b/NugetLikeAPro/TemporaryFile/Properties/AssemblyInfo.cs
index 7c3c830..bf494d8 100644
--- a/NugetLikeAPro/TemporaryFile/Properties/AssemblyInfo.cs
+++ b/NugetLikeAPro/TemporaryFile/Properties/AssemblyInfo.cs
@@ -2,14 +2,14 @@
 using System.Runtime.InteropServices;

 [assembly: AssemblyTitle("TemporaryFile")]
-[assembly: AssemblyDescription("")]
+[assembly: AssemblyDescription("A file that deletes itself when disposed")]
 [assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
+[assembly: AssemblyCompany("ACME Co.")]
 [assembly: AssemblyProduct("TemporaryFile")]
-[assembly: AssemblyCopyright("Copyright ©  2013")]
+[assembly: AssemblyCopyright("Copyright © Jim Counts 2013")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
 [assembly: ComVisible(false)]
 [assembly: Guid("4365a184-3046-4e59-ba28-0eeaaa41e795")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
\ No newline at end of file
+[assembly: AssemblyVersion("1.0.0")]
+[assembly: AssemblyFileVersion("0.0.1")]
\ No newline at end of file

I filled or edited out the attributes which NuGet checks when looking for configuration information: AssemblyDescription, AssemblyCompany. AssemblyCopyright and AssemblyVersion.  I also changed AssemblyFileVersion, even though NuGet doesn’t use it, and I left AssemblyTitle alone because I was happy with the value already there.  After building again, these changes should show up in the newly created package.

An Image of the NuGet Package Explorer showing updated metadata
Most AssemblyInfo changes are applied automatically

NuGet applied most of my changes automatically, and all the build warnings are gone.  But I expected a 2-dot version number both in the package name and as part of the metadata.  That 3-dot version is still hanging around.  I can take greater control over the version number, as well as many other aspects of the package metadata by providing a “nuspec” metadata file.  If this file has the same name as my project and is in the same directory as my project, then NuGet will prefer to use the data from the nuspec.

Pull the Nuspec File Out

You can generate nuspec files from assemblies or project files using NuGet.exe.  In the past I’ve found this method for creating nuspec files to be tedious because it creates configuration  I don’t always need or configuration with boilerplate text that I need to delete.  My old solution was some fairly complex MSBuild scripts that transformed generated files, but today I just create the default package as described above, rip it’s metadata, then customize to my liking.  If you have NuGet Package Explorer open, it’s pretty easy to use the “Save Metadata As…” menu item under “File” and save the nuspec file next to your project file (remove the version number from the filename if you do this).

Another way to retrieve the package nuspec file is with an unzip tool.  NuGet packages are zip files, and tools like 7-zip recognize this, buy you can always change the extension from nupkg to zip, if 7-zip isn’t handy.   Once the file has a zip extension, any zip utility can manipulate it, including the native support built into Windows.

An image showing the nuget package as a zip, open in Windows FIle Explorer
Nupkg files are Zip files

You can extract all the files from the zip, or just the nuspec file.  You will only need the nuspec file.

Put the Nuspec File Next to the Project

Once you have pulled the nuspec file out of the existing package, move it to the project directory.  It should sit in the same folder where the csproj file is (or vbproj, or fsproj) and have the same base name as the csproj.  There should be no version number in the nuspec file name, so remove it if there is.

An image showing the nuspec file in the project folder.
Put the nuspec file in the project folder

You can also add the item to the project using Visual Studio for easy access from the IDE, but it is not required.  I usually add it.

Make Changes

Now, let’s take a look at what is inside the nuspec file.

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
 <metadata>
 <id>TemporaryFile</id>
 <version>1.0.0.0</version>
 <title>TemporaryFile</title>
 <authors>ACME Co.</authors>
 <owners>ACME Co.</owners>
 <requireLicenseAcceptance>false</requireLicenseAcceptance>
 <description>A file that deletes itself when disposed</description>
 <copyright>Copyright © Jim Counts 2013</copyright>
 <dependencies>
 <dependency id="ApprovalUtilities" version="3.0.5" />
 </dependencies>
 </metadata>
</package>

We can see that most of the information in the nuspec file is the exact information displayed in the package explorer. I can now override the defaults by editing this file.  Any XML or text editor will work, it’s very convenient to use Visual Studio if you add the nuspec file to the project, so that’s what I usually do.

diff --git a/NugetLikeAPro/TemporaryFile/TemporaryFile.nuspec b/NugetLikeAPro/TemporaryFile/TemporaryFile.nuspec
index 5770b72..815c44e 100644
--- a/NugetLikeAPro/TemporaryFile/TemporaryFile.nuspec
+++ b/NugetLikeAPro/TemporaryFile/TemporaryFile.nuspec
@@ -2,15 +2,12 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
 <metadata>
 <id>TemporaryFile</id>
- <version>1.0.0.0</version>
+ <version>0.0.1</version>
 <title>TemporaryFile</title>
- <authors>ACME Co.</authors>
+ <authors>@jamesrcounts</authors>
 <owners>ACME Co.</owners>
 <requireLicenseAcceptance>false</requireLicenseAcceptance>
 <description>A file that deletes itself when disposed</description>
 <copyright>Copyright © Jim Counts 2013</copyright>
- <dependencies>
- <dependency id="ApprovalUtilities" version="3.0.5" />
- </dependencies>
 </metadata>
 </package>
\ No newline at end of file

I changed the version number to “0.0.1” and updated the the author to use my twitter handle.  “ACME Co.” is still the owner, and I removed the dependency list.  I prefer to allow NuGet to continue to infer this information on it’s own.

With these changes, the next package I build should reflect the new version number in the file name, and show updated metadata for Version and Authors.  However, the dependency list should remain the same in the completed package.

An image of Nuget Package Explorer showing the applied customizations
That’s More Like It

Automate

You’ll need some way to share your package now that you’ve created one.  If it’s an open source project you can definitely upload it to nuget.org if you like.  For private code, that’s probably not a good idea.  There are solutions out there, and I wrote about one of them in a previous article: Use ProGet to Host Your Private Packages.  In the interest of making sure this article doesn’t get any longer than it already is, I won’t cover options for sharing private packages here.

However, there are a couple things you can do now which will make your life easier once you do start sharing your package.  First, nuget.targets does not clean up after itself during clean and rebuild.  This means that all your old package versions will hang around in the build folder until you delete them manually.  Besides taking up space, those packages eventually slow you down when you get ready to share.  If you are using the NuGet Package Explorer to share, you have to scroll past an increasingly longer list of old package versions to find the new version you want to upload, and if you use the command line utility, all those old versions increase the amount of typing and tabbing in order to complete the command.  Finally, I find the quickest way to push packages is with a custom script which wraps the command line utility, and that script is much easier to write when the bin folder only contains the latest package.

Cleanup with nuget.targets

To integrate nuget.target with “Clean” and “Rebuild” you need to add a new target to the script, add a new item group which lists the files to clean, and finally ad a hook using the “CleanDependsOn” property that will actually execute the target.

Nuget.targets is already added to your solution in the .nuget folder, open it and add what you need.

diff --git a/NugetLikeAPro/.nuget/NuGet.targets b/NugetLikeAPro/.nuget/NuGet.targets
index 8962872..a5cebf3 100644
--- a/NugetLikeAPro/.nuget/NuGet.targets
+++ b/NugetLikeAPro/.nuget/NuGet.targets
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <OutputPackages Include="$(TargetDir)*.nupkg" />
+ </ItemGroup>
 <PropertyGroup>
 <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>

@@ -83,6 +86,11 @@
 $(BuildDependsOn);
 BuildPackage;
 </BuildDependsOn>
+
+ <CleanDependsOn Condition="$(BuildPackage) == 'true'">
+ $(CleanDependsOn);
+ CleanPackages;
+ </CleanDependsOn>
 </PropertyGroup>

 <Target Name="CheckPrerequisites">
@@ -118,6 +126,10 @@
 Condition=" '$(OS)' == 'Windows_NT' " />
 </Target>

+ <Target Name="CleanPackages">
+ <Delete Files="@(OutputPackages)"></Delete>
+ </Target>
+
 <UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
 <ParameterGroup>
 <OutputFilename ParameterType="System.String" Required="true" />

On lines 8-10 I define a collection of items called “OutputPackages” which uses a glob to find all the NuGet packages in the bin directory, referred to in the script as TargetDir.

Then I use this item collection with the new target defined on lines 30-32.  The CleanPackages target is a very simple target that uses MSBuild’s built-in Delete task to remove the files in the OutptuPackages collection.

Finally, I instruct MSBuild to run this target during clean by hooking into the CleanDependsOn property using lines 19-22.  CleanDependsOn is one of several hooks provided for modifying targets defined in Microsoft.Common.Targets. On line 20, I add back any existing dependencies and on line 21 I append the CleanPackages target to the end of the list.  Now, MSBuild will clean up old packages whenever I Clean or Rebuild my project.

Write a push script

Pushing your packages to NuGet.org is pretty simple because it is the default for nuget.exe.  Both NuGet.exe and the NuGet Package Explorer will allow you to specify a custom host to push your package to, but I’m paranoid that I will forget to specify the host and send packages to nuget.org that I don’t want to share publicly.

So, to speed things up, and to keep the risk of mistakes to a minimum, I use a simple shell script to push my packages.  Here is an example that would push to a local ProGet server.

.nuget\NuGet.exe push .\TemporaryFile\bin\Debug\*.nupkg -apikey Admin:Admin -source http://localhost:81/nuget/Default

I specified ProGet’s default credentials as the API key, but if you plan to push to nuget.org I suggest you use the NuGet “setapikey” option to configure the API key on your machine, and that way you don’t have to commit the key to source control.

Recap

In this post I showed how to create basic packages with MSBuild, customize them, and gave a couple automation tips I find useful.  Once you have converted a few of projects to produce packages this way, you can do the conversion in about 15 minutes for straightforward packages.  NuGet packages can become complex and you may need to do a lot more in the customization stage.  However, for most cases I find that these few steps are enough: enable package restore, add the BuildPackage property, rip the nuspec file from the first package, customize a few pieces of AssemblyInfo and nuspec metadata, and start sharing your package.

Once you have the package, you can quit, or you can make your life a little easier by adding a cleanup target and a push script.  Either way, I hope you find this information useful, and bit more approachable than my previous post on this topic.

The GitHub repository to accompany this post is here: https://github.com/jamesrcounts/Blogs/tree/master/NugetLikeAPro

7 thoughts on “NuGet like a Pro, the MSBuild way

  1. Everything worked great. Thank you! However the clean / rebuild didn’t work, packages remained in the bin directory and did not get cleaned. I am using vs2013

    1. clean/rebuild don’t delete .nupkg files, the rest works as expected.
      I’m also using VS2013.
      Any ideas why is this happening?

  2. Hi Jim

    You wrote:

    ‘what to do with this package and how to configure Visual Studio to use it are topics better handled on their own, so I wont cover them further here. Stay tuned.’

    Well, that’s exactly the part I’m interested in ! How do I tell VIsual Studio to use the symbols packages instead of the the ones without debugging infos ? Could you tell me that ?

  3. I forgot to mention I don’t want to use a symbol server. I work locally for now, and all I do is publishing the packages to a local folder. But VS restore packages without the symbols data, ignoring the ones with symbols data.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s