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

Advertisements
Coding

Keep It Secret, Keep It Safe

imageI had a problem with an open source project recently.  I checked out the source, did some research and I thought I had a pretty good idea how to solve the problem effecting me.  I sent the maintainer a patch, and he graciously invited me to do some pair programming on the issue.  Cool.

Now, the project in question is signed, and during my initial research I had trouble building it because I don’t have the signing key.  So, without really thinking about it (since I don’t have check-in rights on the project) I simply reconfigured the assemblies to build without signing enabled.  When we paired today, we ended up sharing my screen and using my working copy.  At the end of the session, we pushed from my copy and he used his credentials to authenticate.  The changes to AssemblyInfo.cs were inadvertently included as well.

I sent him an email explaining what happened and I’m sure the inadvertent changes will be reverted soon, but I naturally wondered what I could have done differently to prevent the problem.  Naturally, we could have both taken a closer look at the change set we were about to check in.  Before that, I could have been more careful with SVN.  We did run an update before starting, but I should also have cleaned the working copy.  Or, I could have just deleted the working copy and started with a fresh checkout.  Ok, so those are good habits to have, but is there any permanent change that would have prevented the problem even if I forgot to do any of that?

What I Could Do

I thought of the easy solution last.  Although the signing key is not checked into the repository, it’s expected name and location are known, since these values are referred to in the AssemblyInfo.cs file (I know because that’s the information I deleted!).  So the easiest thing to do would be for me to create a new key pair with the same filename, and put it in the expected location.  Visual Studio will be happy to compile the project, since it doesn’t actually know or care what key pair is used.

This solution is fine for testing/debugging purposes.  If I happen to go rouge and try to publish my own copies of the assemblies, the public keys will be different.  Anyone who cares enough can still distinguish between assemblies signed by me and those signed by the maintainer.  Since the key pair is not in the repository, it’s a “non-versioned” file and it will not be included in the change set if/when we check-in changes from my working copy.  I like this solution because I can implement it without bothering the maintainer.

What the Maintainer Could Do

If the maintainer wanted to make the project more idiot-proof, he could integrate some changes into the .cspoj files, MSBuild is here help.  Instead of specifying the signing key location in the AssemblyInfo.cs file using the AssemblyKeyFileAttribute, the location can be specified using the AssemblyOriginatorKeyFile property.  Once MSBuild knows the expected key file location, it can check that the file exists, and use that information to control the SignAssembly property.

Here’s an example:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
         DefaultTargets="Build"
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <!-- Other properties omitted -->
    <AssemblyOriginatorKeyFile>..\SecretKeyPair.snk</AssemblyOriginatorKeyFile>
  </PropertyGroup>
  <Choose>
    <When Condition="Exists($(AssemblyOriginatorKeyFile))">
      <PropertyGroup>
        <SignAssembly>true</SignAssembly>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup>
        <SignAssembly>false</SignAssembly>
      </PropertyGroup>
    </Otherwise>
  </Choose>

When Visual Studio starts, it will evaluate this condition once.  If the key is found, SignAssembly will become true; otherwise it will be false.  Moving, renaming, or deleting the file after Visual Studio is running will result in a build error when the IDE tries to sign the assembly.  Similarly, if the file is missing when Visual Studio starts, it will not try to sign the assembly, even if the key is copied to the correct location later.  The condition is not checked at build time, but at load time.  (You could also manually unload/load the project file from Visual Studio.)

I like this solution because its more idiot proof.  The project file adapts to it’s environment, the maintainer has the key and as a result will produce signed assemblies from his working copy.  Meanwhile, contributors can debug and test changes without even having to worry about creating their own signing key in order to build.

Either solution should work.

Coding

Manage Your MEF Parts with NuGet

Why?

With MEF you have the flexibility to compose applications without taking a reference to every required assembly. MEF’s flexibility is one of its most seductive features. But I see a dark side. Maybe I’m paranoid, but I miss the security of taking references. Before MEF, I never worried about getting my assemblies into the bin directory because MSBuild took care of that for me. Now I do worry about it, a little, and this is one of the reasons I’ve written about a method to test MEF composition.

I also really like NuGet. Most people compare NuGet to apt, but to me it’s always been “CPAN for .NET”. I guess that tells you a little bit about where I’m coming form. But whatever your background, you have to love a system that lets you say “Here is the thing I’m interested in using, you go get the rest.” But at first glance, MEF and NuGet don’t play well together (even though NuGet uses MEF!) NuGet makes the fundamental assumption that when a library exists in a NuGet package, then the intent when installing that package is to reference that library. I’m not saying that’s a bad assumption in general, it’s just not a very good assumption to make when dealing with libraries of MEF parts.

The good news is that NuGet can be used to create MEF friendly packages that don’t create references when installed, but do ensure that the parts are copied to the bin directory. You get runtime-only references, with all the NuGet package management goodness. I’ll be the first to admit that some of the techniques used to make this happen are hacky, but I’ve tested it and it works for me. So strap yourself in, and read on.

A Little Background

I’m going to start by showing the structure of a nuspec file which NuGet can use to make a MEF friendly package.

After showing the nuspec, I’ll show how to automate spec and package creation using MSBuild.

  • It will be helpful to know a little MSBuild. I wish I had a good resource, but I don’t. Somehow, I’ve just learned it though osmosis. This one looks nice. I mostly look at the reference when I have questions.
  • To create the MEF-friendly package, I’ll extend the recipe I wrote for creating a “Standard” NuGet package with MSBuild.
  • You will need the nightly build of the MSBuild Community Tasks.
  • There will be some XSLT. I can vouch for w3schools, since I used it as my primary reference for creating the XSLT used in this post.

Anatomy of a MEF Friendly Package

Here is the nuspec which builds a MEF friendly package for the CheesePizzaMaker assembly from my MEF talk.

<?xml version="1.0" encoding="utf-8"?>
<package>
  <metadata>
    <id>CheesePizzaMaker.MEF</id>
    <version>1.0.4441.28470</version>
    <authors>Jim Counts</authors>
    <owners>Jim Counts</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Makes Cheese Pizza</description>
    <copyright>Copyright 2012</copyright>
    <title>CheesePizzaMaker (MEF)</title>
    <dependencies>
      <dependency id="PizzaStore" version="" />
    </dependencies>
    <frameworkAssemblies>
      <frameworkAssembly assemblyName="System" targetFramework="" />
      <frameworkAssembly assemblyName="System.ComponentModel.Composition" targetFramework="" />
    </frameworkAssemblies>
  </metadata>
  <files>
    <file src="bin\x86\Release\CheesePizzaMaker.dll" target="content\Parts" />
    <file src="bin\x86\Release\CodeContracts\CheesePizzaMaker.Contracts.dll" target="content\Parts" />
    <file src="bin\x86\Release\CheesePizzaMaker.pdb" target="content\Parts" />
    <file src="NuGet\install.ps1" target="tools" />
  </files>
</package>

This nuspec file is meant to be the target of the “nuget pack” command. Since the target will not be a project file, NuGet will not fill any of the fields for us at runtime, so there are no replacement tokens like there were in the “standard” nuspec I wrote about before. But, you can avoid filling out most of the data by hand if you give NuGet an assembly to examine when it runs the “nuget spec” command. I’ll show you how to do that later in this post, or you can consult the NuGet docs. After generating the spec, I customized some fields:

  • Id – to distinguish the MEF friendly package, I added .MEF to the Id.
  • Title – I added the title field, and used a title that made it easy to distinguish in the Package Manager
  • Dependencies – I added the PizzaStore package as a dependency. This package contains the interface definitions shared between parts and their host. When the version field is blank, NuGet will look for the highest available version, and that’s the behavior I want, but you can specify a version if you like. Also, note that the PizzaStore package is a standard package; it will create a reference in the project where it is installed. Since it only contains interfaces, and not parts this is what we want. However, you could (in theory, I haven’t tried this yet) have MEF packages that depend on other MEF packages.
  • Framework Assemblies – Since this is a MEF friendly package, we know that the target project will need MEF. So, System.ComponentModel.Composition and System are both listed as dependencies. NuGet will add them to the project if they aren’t already there. You could leave this off, but then you would have to add the references by hand. Automation is smoother, take advantage of it.
  • Files – Finally we have the files section. We have the same three files that I included in the standard package, and one more file, which is the install.ps1 script which runs when the package is installed. Other than the PowerShell script, which we will look at in a moment, the big difference is where the files are located in the package. Instead of placing the files in the lib folder, as is standard, I’ve put them into the content folder. Furthermore, I’ve placed them in a subdirectory called “Parts”.

Because the libraries are in the content folder, NuGet will not add a reference to the files when the package is installed. However, it won’t copy them to the bin directory either, so placing the files into the content folder only solves half the problem. Install.ps1 solves the other half:

param($installPath, $toolsPath, $package, $project)

foreach ($runtimelib in $project.ProjectItems.Item("parts").ProjectItems)
{
    if(-not $runtimeLib.Name.EndsWith(".Contracts.dll"))
    {
        # Set to "Copy Always"
        $runtimelib.Properties.Item("CopyToOutputDirectory").Value = 1
    }
    else
    {
        # Change to "None"
        $runtimelib.Properties.Item("BuildAction").Value = 0;
    }
}

To make the MEF friendly package work, I’ve adopted the convention that the Parts folder contains MEF parts which should be copied to the bin directory. The only exception is the Contracts library, which also contains the MEF attributes for the assemblies they describe, and can cause cardinality mismatches if it is deployed to bin. By default, content files are placed directly in the project folder, the build action is set to “Content”, and the copy to output directory property is set to “Do not copy”. Because we specified a subfolder in the nuspec file, NuGet will create a “Parts” directory and place the files in there. To get the files copied to the bin directory, the PowerShell script enumerates through the contents of the Parts folder (which is a “ProjectItem” from MSBuild’s point of view) and change the value of the “CopyToOutputDirectory” property to 1 as long as the file doesn’t end with “.Contracts.dll.” In the enumeration for “CopyToOutputDirectory”, 1 means “Copy Always”, good luck on finding that documented anywhere (send me a link if you find it.)

In theory, the top half of the if-statement is all you need to make the package MEF friendly. But, I discovered that MSI files will include the Contracts library if you leave the Build Action equal to “Content.” Again, this will cause cardinality mismatch exceptions if you then use the MSI to install your application. What’s worse is that you will only see the problem in production. So, to save us from this headache, the PowerShell script will set the Build Action equal to “None” when it finds the Contracts library.

So that’s a MEF friendly NuGet package. When you install the package, your libraries will appear in the Parts folder. On build, your assembly and its pdb will be copied to a “Parts” folder in the bin directory. The only adjustment you need to make in your application is that you must include a DirectoryCatalog for this new subfolder in your composition. You might also have noted the hardcoded value for the package version. In the next section I’ll show how this number can be updated at runtime, but if you don’t want to read that, just know that this value can be overridden from the command line when calling “nuget pack”.

Automating with MSBuild

I’m sensitive to the needs of those who like to cut and paste, so here is a link to the NuGet folder for the CheesePizzaMaker on bitbucket.org. Do come back though, because automating the MEF friendly package is a little more complex than the standard package.

Consuming the Build Script in the Project

This step is just like consuming the standard build script.

  1. Unload your project.
  2. Edit your project.
  3. Update your project file’s “AfterBuild” target.

If you are unfamiliar with editing a project file, visit my previous article on creating NuGet packages with MSBuild for step-by-step instructions. If you are planning to build both types of packages (standard and MEF-friendly) then your AfterBuild target should look like this when you are done:

  <Target Name="AfterBuild">
    <MSBuild Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"
       Projects="NuGet\NuGet.msbuild" />
    <MSBuild Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"
       Projects="NuGet\NuGet.MEF.msbuild" />
  </Target>

Setup Support Files

We need to create several supporting files, two that enable the build script to customize the nuspec file for us, and one which we include in the nuget package. Finally, we include NuGet itself, for convenience (as discussed previously, this is my personal preference, but the build script will need to be adjusted if your preference differs.) You don’t need to add these files to the solution, but if you do, make sure that you set the Build Action to “None”.

  • NuGet.exe – Go ahead and create a folder called NuGet in your project, and save a copy of NuGet.exe in this folder.
  • Install.ps1 – This file is included in the MEF-friendly NuGet package tools folder. The generated nuspec will expect to find this file in the NuGet subfolder. So create a text file called install.ps1 and copy the contents of the PowerShell script into it (shown above and available on bitbucket.org).
  • Mefdata.xml – This file is a source file for an XmlMassUpdate task in the MEF-friendly build script. It contains all the custom elements except the dependencies. Some of the data is dummy data, which the script will update in a later step. Create this file in the NuGet folder.
<?xml version="1.0" encoding="utf-8"?>
<package xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
  <metadata>
    <title>title</title>
    <frameworkAssemblies>
      <frameworkAssembly xmu:key="assemblyName"
         assemblyName="System" targetFramework="" />
      <frameworkAssembly xmu:key="assemblyName"
         assemblyName="System.ComponentModel.Composition" targetFramework="" />
    </frameworkAssemblies>
  </metadata>
  <files>
    <file xmu:key="src" src="AssemblyPath" target="content\Parts" />
    <file xmu:key="src" src="ContractPath" target="content\Parts" />
    <file xmu:key="src" src="SymbolPath" target="content\Parts" />
    <file xmu:key="src" src="NuGet\install.ps1" target="tools" />
  </files>
</package>
  • dependencydata.xslt – If the project has a “packages.config” file then it means that there are some NuGet packages installed in the project. The script uses this transform file to convert packages.config into an XmlMassUpdate source which contains the contents of the <dependencies /> node in the nuspec file. Create this file in the NuGet folder.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                exclude-result-prefixes="msxsl"
>
  <xsl:output indent="yes"/>
  <xsl:template match="/">
    <package xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
      <metadata>
        <dependencies>
          <xsl:for-each select="packages/package">
            <dependency xmu:key="id">
              <xsl:attribute name="id">
                <xsl:value-of select="@id"/>
              </xsl:attribute>
              <xsl:attribute name="version"/>
            </dependency>
          </xsl:for-each>
        </dependencies>
      </metadata>
    </package>
  </xsl:template>
</xsl:stylesheet>

Now we’re ready to create the NuGet.MEF.msbuild script.

Create the Build Script

Create an XML file in the NuGet folder and call it NuGet.MEF.msbuild. You can download the complete file from bitbucket.org, but I will go through each section step by step here.

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
         ToolsVersion="4.0"
         DefaultTargets="default">
  <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
  <PropertyGroup>
    <NuGetApp>NuGet.exe</NuGetApp>
    <ProjectDir>$(MSBuildProjectDirectory)\..</ProjectDir>
    <PackageDir>$(ProjectDir)\..\..\..\PackageSource</PackageDir>
    <ReleasePath>bin\x86\Release</ReleasePath>
    <ProjectName>CheesePizzaMaker</ProjectName>
    <PackagesFile>$(ProjectDir)\packages.config</PackagesFile>
    <DependencyFile>$(MSBuildProjectDirectory)\dependencydata.xml</DependencyFile>
    <MefDataFile>$(MSBuildProjectDirectory)\mefdata.xml</MefDataFile>
    <XsltFile>$(MSBuildProjectDirectory)\dependencydata.xslt</XsltFile>
    <PackageId>$(ProjectName).MEF</PackageId>
    <MefNuSpecFile>$(ProjectDir)\$(PackageId).nuspec</MefNuSpecFile>
    <ReferenceLib>$(ReleasePath)\$(ProjectName).dll</ReferenceLib>
    <ContractLib>$(ReleasePath)\CodeContracts\$(ProjectName).Contracts.dll</ContractLib>
    <SymbolLib>$(ReleasePath)\$(ProjectName).pdb</SymbolLib>
  </PropertyGroup>

We start off by creating our project and importing the MSBuild Community Tasks. Next we setup some data used throughout the script. Some of these properties aren’t as clean as I’d like them to be, so there might be some refactoring in my future. Here are the highlights:

  • PackageDir – the script will move completed packages to this folder at the end of the process.
  • ReleasePath – relative path to the bin directory to pull files from.
  • ProjectName – if you don’t make any customizations to this process, then this is the only field you need to worry about updating.
  • PackageId – my convention is to suffix “.MEF” to the project name. You can setup your own convention here.
  • MefNuSpecFile – this is where the build script will put the customized nuspec file when it is done writing it.
  • ReferenceLib – this is the part library you want to package.
  • ContractLib – the Code Contracts reference assembly is optional, but you need to make some adjustments to this recipe if you don’t want to include it.
  • SymbolLib – the debugging symbols, also optional, but it’s useful to me in my packages.

The first targets follow the property section:

  <Target Name="clean">
    <Delete Files="$(PackageId).nuspec; ..\$(PackageId).nuspec; $(ProjectName).nuspec; $(DependencyFile)" />
  </Target>
  <Target Name="default" DependsOnTargets="MefSpec; MefPackage; MovePackages"/>

During script development, it’s nice to be able to reset everything, so the clean target will delete intermediate and output files. You can invoke it using the /t switch on the command line.

C:\PizzaStore\CheesePizzaMaker\NuGet>c:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe NuGet.MEF.msbuild /t:clean

The default target specifies which targets must be executed to build and deploy the package. There is nothing special about the name “default”. It is only the default target because it is specified in the DefaultTargets attribute in the project element. We could specify “clean” or “MefSpec” as the default, but that wouldn’t make much sense.

  <Target Name="MefSpec"
          Condition="!Exists($(MefNuSpecFile))"
          DependsOnTargets="CreateDependencyFile; CreateMefSpec; CleanMefSpec; MergeMefSpec; UpdateMefSpec">
  </Target>

Like “default”, the MefSpec target is a meta-target which specifies other targets to run. It also includes a condition which will skip the spec creation if a spec already exists. This means that you can further customize the generated spec by hand without worrying about overwriting your changes on the next build.

  <Target Name="CreateDependencyFile" Condition="Exists($(PackagesFile))">
    <XslTransformation
      OutputPaths="$(DependencyFile)"
      XmlInputPaths="$(PackagesFile)"
      XslInputPath="$(XsltFile)"
      />
  </Target>

CreateDependencyFile executes an XslTransformation task when “packages.config” exists. It applies dependencydata.xslt to packages.config and creates dependencydata.xml, which is an XmlMassUpdate source file.

  <Target Name="CreateMefSpec">
    <Exec WorkingDirectory="$(MSBuildProjectDirectory)"
          Command="$(NuGetApp) spec -AssemblyPath &quot;$(ProjectDir)\$(ReferenceLib)&quot;" />
    <Move SourceFiles="$(ProjectName).nuspec"
          DestinationFiles="$(MefNuSpecFile)" />
  </Target>

CreateMefSpec executes “nuget spec” to create a default spec file. We give NuGet the path to the parts assembly so that it can fill in some of the standard data for us. Note that this command is executed from the “NuGet” subfolder for two reasons. First, we want to avoid overwriting the standard nuspec file if it exists, I couldn’t see a way to specify the output file name from the NuGet command line. Second, NuGet will automatically use the csproj file to generate a tokenized spec file for us if we run the spec command from a directory which contains a csproj (or any other project) file. We don’t want a tokenized spec file, so we need to run from a folder where NuGet can’t see the project file. Ultimately, we want the MEF-friendly spec file in the project folder, so we move it there before finishing CreateMefSpec.

  <Target Name="CleanMefSpec">
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/metadata/licenseUrl"
               Delete="true" />
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/metadata/projectUrl"
               Delete="true" />
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/metadata/iconUrl"
               Delete="true" />
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/metadata/releaseNotes"
               Delete="true" />
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/metadata/tags"
               Delete="true" />
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/metadata/dependencies"
               Delete="true" />
  </Target>

In CleanMefSpec, I delete fields I don’t care about, and one field I intend to replace with XmlMassUpdate. If you have use of the different URLs, notes, etc., then you should not delete these fields, but update them in the UpdateMefSpec target.

  <Target Name="MergeMefSpec">
    <XmlMassUpdate Condition="Exists($(DependencyFile))"
       ContentFile="$(MefNuSpecFile)"
                   ContentRoot="/"
                   SubstitutionsFile="$(DependencyFile)"
                   SubstitutionsRoot="/" />
    <XmlMassUpdate Condition="Exists($(MefDataFile))"
                   ContentFile="$(MefNuSpecFile)"
                   ContentRoot="/"
                   SubstitutionsFile="$(MefDataFile)"
                   SubstitutionsRoot="/" />
  </Target>

MergeMefSpec performs one or two XmlMassUpdate tasks. If there was a packages.config file, then there should now be a dependencydata.xml file created by the “CreateDependencyFile” target. If so, MergeMefSpec will update the MEF-friendly nuspec with the dependencies. You might want to review the dependencies after the spec is written. This technique does no interdependency analysis of the packages in packages.config. It just takes a dependency on all of them. So if you are writing package A, and A depends on B, and B depends on C, then your packages.config will list B and C as installed packages. However, package A may not need a direct dependency on C. You can imagine a scenario where B is updated and no longer requires C, in which case package A doesn’t need to include it any more either, but your spec doesn’t know that. If you are worried about this scenario, your current option is to edit the spec by hand to remove the indirect dependencies. This is one of the hacky areas I would like to revisit in the future, but for now just be aware of it.

Next, MergeMefSpec uses XmlMassUpdate to merge in the “static” customizations from mefdata.xml that don’t depend on whether or not there are other NuGet packages installed in the project. At this point we have a template nuspec which needs to be updated with relevant data from the project.

  <Target Name="UpdateMefSpec">
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/metadata/id"
               Value="$(PackageId)" />
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/metadata/title"
               Value="$(ProjectName) (MEF)" />
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
              XPath="/package/files/file[@src='AssemblyPath']/@src"
               Value="$(ReferenceLib)" />
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/files/file[@src='ContractPath']/@src"
               Value="$(ContractLib)" />
    <XmlUpdate XmlFileName="$(MefNuSpecFile)"
               XPath="/package/files/file[@src='SymbolPath']/@src"
               Value="$(SymbolLib)" />
  </Target>

UpdateMefSpec replaces some defaults and dummy data with the real values. Here we update the id, the title, and the paths to the package files. The custom nuspec is now complete.

  <Target Name="MefPackage"
          Condition="Exists($(MefNuSpecFile))">
    <!-- Get the version number of the main assembly to pass to nuget cli -->
    <GetAssemblyIdentity AssemblyFiles="$(ProjectDir)\$(ReferenceLib)">
      <Output TaskParameter="Assemblies"
              ItemName="AsmInfo" />
    </GetAssemblyIdentity>
    <Exec WorkingDirectory="$(ProjectDir)"
          Command="&quot;$(MSBuildProjectDirectory)\$(NuGetApp)&quot; pack &quot;$(MefNuSpecFile)&quot; -Verbose -NoPackageAnalysis -Version %(AsmInfo.Version)" />
  </Target>

Next up is MefPackage, where we build the MEF-friendly package. MefPackage first examines the parts assembly using a GetAssemblyIdentity task. This task places the assembly information into the property specified by the ItemName attribute; in our case it is called “AsmInfo”. Next we execute “nuget pack”. Our target is the nuspec file. I use Verbose because I like it. NuGet will not be happy that we have put dll files in the content folder and issues warnings suggesting that we might want to put them in a lib folder. Since the whole point of this exercise is to distribute libraries as content rather than references, I’ve suppressed the analysis step using NoPackageAnalysis. Finally, we specify the Version flag and give it the version data supplied by the GetAssemblyIdentity task.

  <Target Name="MovePackages"
          Condition="Exists($(PackageDir))">
    <!-- Using command line because I want to be sure to get the most up to date list of *.nupkg -->
    <Exec WorkingDirectory="$(ProjectDir)"
          Command="move /y *.nupkg &quot;$(PackageDir)&quot;" />
  </Target>
</Project>

As with the standard nuget build script, MovePackages checks that the destination directory exists then uses the shell to move the packages to the destination. I found that it was much easier to use the shell because it will get the most recent list of *.nupkg files in the source folder, and will happily overwrite packages in the destination folder. Finally, we close the project element and the MSBuild script is done.

Test the Build

Switch to release mode and build your project. If everything is setup correctly, your packages should appear in the folder specified by the PackageDir property. If not, see my previous post for troubleshooting tips.

Test the Package

In the PizzaStore example, there is a dummy program that does nothing but serves as a place to simulate different composition scenarios. To test the package I removed the project reference to PizzaStore. There was no reference to CheesePizzaMaker since I used .bat files to move the dll files around during the MEF talk.

After removing the PizzaStore reference I setup Visual Studio to use PackageDir as a package source, and then used the NuGet package manager in GUI mode to install the MEF package. As expected, NuGet created the Parts folder, set the Build Action and other properties, and also pulled in the dependency to PizzaStore and re-created the reference (this time as an assembly reference rather than a project reference).

Uninstalling the CheesePizzaMaker.MEF package also removed all the content files and the assembly reference.

Thanks for Reading

There you have it: MEF, now with NuGet. I’m sure there is a candy bar pun in there somewhere.

Coding

Creating Packages with NuGet the MSBuild Way

Authors Note:

Hello!  Thanks for visiting this page.  Unfortunately it is very out of date.  Unless you have unusual needs you don’t need to jump through all the steps described in this post.  I don’t make packages this way anymore, and neither should you. Instead, check out my updated post: NuGet Like A Pro, the MSBuild Way.

The new post provides a greatly streamlined process, and this post remains here for historical purposes, or extreme edge cases.

Which ever post you choose, thanks for reading!

There are quite a few ways to integrate NuGet into your build process:

  • Check out this article on NuGetter if you use TFS.
  • Here is an article on integrating with TeamCity by Scott Hanselman. This one has many great resources, including a video from his TechEd talk that goes with the article.
  • Here’s a github:gist that shows how to use a windows command shell batch file to build a package, also shows a ruby script.
  • Finally, here is the reference at docs.nuget.org that explains several scenarios that NuGet.exe understands.

I use CruiseControl.NET and reading the reference at docs.nuget.org was enough info for me to figure out how to setup NuGet as a post build event, but the solution felt hacky. I searched the web and found a few pages like those referenced above. In the end, I found that my solution was pretty much state of the art.

Even so, I wasn’t quite satisfied:

  1. Creating the nuspec file wasn’t very fun. When there was no nuspec, the script used NuGet.exe to create the file and launched notepad so that I could edit it. That’s hacky, and it didn’t take long to get tired of making the same edits to every nuspec. After editing the file, I had to run the build again to make the package.
  2. I found the default packages unsatisfying. The nuspec file does not include everything I wanted. For example, I wanted to include the .pdb file. I looked at the “symbols” option in NuGet.exe but it was both more and less than I wanted. Although symbolsource.org looks nice, I don’t want to involve a third party just so I can step through my own code.  I also use Code Contracts. I wanted the Contract reference assemblies included in the package too.
  3. I wanted more control over the versioning. As I mentioned before, I have a scenario where I need more than one package configuration for the same assembly. The quick and dirty package building process gathers version numbers and other tokens from the assembly and combines these with the statically defined data in the nuspec when writing a package. This works great when the target is a csproj, vbproj, fsproj or even nproj (nemerle project!), but does not happen when building from a nuspec. NuGet lets you specify the version on the command line, but you have to figure out how to get the version number to the command line so that NuGet can use it. You can use PowerShell to figure out the assembly version number, but then you have to worry about execution policy and/or signing.

Some of these issues can be resolved by updating the nuspec file. NuGet won’t stomp on an existing nuspec, so that’s nice. Resolving problems in this way means I will have to repeat the process for every assembly I want to package, which makes problem #1 worse. PowerShell did a decent job but I didn’t want to write about it because I don’t want to go into the PowerShell security briar patch. (Side rant: I can build a .NET CLI application that will perform the exact same task as the PS script, and run without altering the execution policy or signing the assembly. I considered writing one and publishing it in a future blog entry, but it turns out there is another way to do this. So in the end, the security policy around PowerShell did not do much to encourage me to start signing PowerShell scripts, it just encouraged me to use something other than PowerShell.)

Integrating NuGet and MSBuild

I am not the first person to do this. Over at Technical Jargon, Jeremy Skinner has an article that shows a scenario you might want to use if your packages are public and you intend to publish them. My solution would not have been possible without his example.  My goal is to create a separate MSBuild file for NuGet and chain it off the end of the Visual Studio project file. I’m going to work my way in, and start by showing how to consume the NuGet.msbuild file from the project file.

Edit Project File

I’m going to use a C# project (one of the PizzaStore projects from my MEF talk), but the same process should work for any Visual Studio project file.

You will need to edit your project file by hand. Once you have opened your project in Visual Studio, right-click on the project node in the Solution Explorer and choose “Unload Project,” then right click again on the unloaded project and select “Edit <projectname>.csproj.”

image

Once the project opens, you will see that it is an XML file. Scroll all the way to the bottom of the file and you should see lines commented lines like these:

  <!-- 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">
 </Target>
 <Target Name="AfterBuild">
 </Target>
 -->

We are going to make use of the “AfterBuild” target. Move it out of the commented block and add the following lines to the element:

<Target Name="AfterBuild">
    <MSBuild Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"
             Projects="NuGet\NuGet.msbuild" />
</Target>

This snippet tells MSBuild (and Visual Studio) to execute a second MSBuild script once the targets in the project file finish building. I have set this up the way I like it, but you might want to do things differently.

  1. I setup a condition. This script will only execute when the build is configured for an x86 Release. I like building x86 assemblies, I like controlling package creating by switching to release mode. You can adjust this to your liking by consulting this MSDN page, or remove the condition entirely if you want.
  2. I plan to place the NuGet.msbuild file in a subfolder of the project. You can put it wherever you like, but you will need to make other adjustments to the paths in NuGet.msbuild if you choose a different location.

That’s it for the project file. Save it. Right click on the project node in Solution Explorer again, and choose “Reload Project.” Visual Studio will ask you if you want to close the XML representation of the project. Click OK and the project should load.

Setup MSBuild Community Tasks

To automate the nuspec file creation, we will need to supplement MSBuild with some better XML rewriting tools. The MSBuild Community Tasks nightly build has everything we need. Do not get the last “release” it seems that has not been updated in a while.

Download the nightly build and install.

Setup NuGet.msbuild

Add a folder called “NuGet” under your project folder. I like to have this folder as part of the project for easy access, so I use the “New Folder” item on the Project menu in Visual Studio to create the folder. You will put two things into this folder.

  1. NuGet.exe. Putting this in the local folder is not 100% necessary. You could put NuGet somewhere in your path, and share it between all your projects. I like to keep a copy with each project, that way I don’t have to remember to setup every build machine with NuGet. I trade disk space for convenience, but it is up to you. My example assumes NuGet is in the folder with the build file. Make sure you set the exe Build Action to “None” if you add the file using Visual Studio.
  2. NuGet.msbuild. Use Visual Studio to add a new XML file to the NuGet folder and call it NuGet.msbuild.

Next we’ll look at the contents of the MSBuild file for the PizzaStore project, section by section.  If you want to cut and paste, you can find the whole file on bitbucket.org.

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
               ToolsVersion="4.0"
               DefaultTargets="default">
  <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />

We start by declaring an MSBuild project and setting the default build target, next we import the community tasks. Immediately after the import element, we declare some properties that we will use later:

  <PropertyGroup>
    <ProjectName>PizzaStore</ProjectName>
    <BaseDir>$(MSBuildProjectDirectory)\..</BaseDir>
    <BuildDir>bin\x86\Release</BuildDir>
    <PackageDir>$(BaseDir)\..\..\..\PackageSource</PackageDir>
    <NuGetApp>NuGet\NuGet.exe</NuGetApp>
    <NuSpecFile>$(BaseDir)\$(ProjectName).nuspec</NuSpecFile>
    <ProjectFile>$(ProjectName).csproj</ProjectFile>
    <ReferenceLib>$(ProjectName).dll</ReferenceLib>
    <ContractLib>
      $(BuildDir)\CodeContracts\$(ProjectName).Contracts.dll
    </ContractLib>
    <SymbolDB>$(BuildDir)\$(ProjectName).pdb</SymbolDB>
  </PropertyGroup>

If you follow this article like a recipe, then the ProjectName property should be the only thing you need to change.  Of course, you are free to tweak things if you know what’s going on. The path to the BuildDir will need updating if you do not use x86 builds. You might want to put the PackageDir somewhere else.

Next, come some XML snippets that the XmlMassUpdate task will merge into the default nuspec file:

<ProjectExtensions>
  <NuGetReferences>
    <references>
      <reference file="$(ReferenceLib)" />
    </references>
  </NuGetReferences>
  <NuGetFiles>
    <files xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
      <file xmu:key="src"
              src="Contracts"
              target="lib\net40" />
      <file xmu:key="src"
              src="Symbols"
              target="lib\net40"/>
    </files>
  </NuGetFiles>
</ProjectExtensions>

The first section will add an explicit reference to the assembly file. By default, NuGet would also add a reference to the Code Contracts assembly, which we do not want. The <reference /> element prevents that behavior. The second section will add two files to the package, one for the Code Contracts assembly, and one for the pdb file. You can do a lot with XmlMassUpdate, but finding the documentation isn’t obvious. You need to download the source and look at the compiled html help file in the documentation folder.

Now we move on to the build targets. First, we declare the default target, which just tells MSBuild which other targets it needs to execute:

<Target Name="default"
   DependsOnTargets="StdSpec; StdPackage; MovePackages"/>
  • StdSpec creates the “standard” (meaning my standard) nuspec file.
  • StdPackage creates the “standard” (again my standard) nupkg file.
  • MovePackages delivers any packages created during the build to the path specified by the PackageDir property.

Here’s the target to create the nuspec file, the condition ensures that it only runs if the nuspec is missing. It is also the longest:

<Target Condition="!Exists($(NuSpecFile))"
        Name="StdSpec">
  <Exec Condition="!Exists($(NuSpecFile))"
        WorkingDirectory="$(BaseDir)"
        Command="$(NuGetApp) spec" />
  <XmlMassUpdate Condition="Exists($(NuSpecFile))"
    ContentFile="$(NuSpecFile)"
    ContentRoot="/package/metadata"
    NamespaceDefinitions="msb=http://schemas.microsoft.com/developer/msbuild/2003"
    SubstitutionsFile="$(MSBuildProjectFullPath)"
    SubstitutionsRoot="/msb:Project/msb:ProjectExtensions/msb:NuGetReferences" />
  <XmlMassUpdate Condition="Exists($(NuSpecFile))"
    ContentFile="$(NuSpecFile)"
    ContentRoot="/package"
    NamespaceDefinitions="msb=http://schemas.microsoft.com/developer/msbuild/2003"
    SubstitutionsFile="$(MSBuildProjectFullPath)"
    SubstitutionsRoot="/msb:Project/msb:ProjectExtensions/msb:NuGetFiles" />
  <XmlUpdate Condition="Exists($(NuSpecFile))"
    XmlFileName="$(NuSpecFile)"
    XPath="/package/metadata/licenseUrl"
    Delete="true" />
  <XmlUpdate Condition="Exists($(NuSpecFile))"
    XmlFileName="$(NuSpecFile)"
    XPath="/package/metadata/projectUrl"
    Delete="true" />
  <XmlUpdate Condition="Exists($(NuSpecFile))"
    XmlFileName="$(NuSpecFile)"
    XPath="/package/metadata/iconUrl"
    Delete="true" />
  <XmlUpdate Condition="Exists($(NuSpecFile))"
    XmlFileName="$(NuSpecFile)"
    XPath="/package/metadata/releaseNotes"
    Delete="true" />
  <XmlUpdate Condition="Exists($(NuSpecFile))"
    XmlFileName="$(NuSpecFile)"
    XPath="/package/metadata/tags"
    Delete="true" />
  <XmlUpdate Condition="Exists($(NuSpecFile))"
    XmlFileName="$(NuSpecFile)"
    XPath="/package/metadata/references/reference/@file"
    Value="$(ReferenceLib)" />
  <XmlUpdate Condition="Exists($(NuSpecFile))"
    XmlFileName="$(NuSpecFile)"
    XPath="/package/files/file[@src='Symbols']/@src"
    Value="$(SymbolDB)" />
  <XmlUpdate Condition="Exists($(NuSpecFile))"
    XmlFileName="$(NuSpecFile)"
    XPath="/package/files/file[@src='Contracts']/@src"
    Value="$(ContractLib)" />
</Target>

First, MSBuild invokes NuGet.exe to generate a nuspec file for the project. XmlMassUpdate tasks merge the two XML snippets we previously specified into the nuspec. Next, several XmlUpdate tasks execute to delete default values from the nuspec file. You need to delete these elements or update them with meaningful information. If you ignore these fields, NuGet will complain when generating the package. I delete them, but if your project needs these fields, you can use a Value attribute to set the value instead of using the Delete attribute to remove the element. Finally, the last three XmlUpdate tasks update the merged XML snippets with meaningful information. When this task finishes, I have a nuspec setup just the way I like, and you can customize the process to your liking as well.

Next, MSBuild creates the package file:

<Target Name="StdPackage">
  <Exec WorkingDirectory="$(BaseDir)"
        Command="$(NuGetApp) pack $(ProjectFile) -Verbose -Prop Configuration=Release" />
</Target>

We invoke Nuget.exe again, and pass it the pack command, targeting the project file. I target the release configuration, and I use the verbose output because I like it.

Finally, the last target and the end of the file:

  <Target Name="MovePackages"
          Condition="Exists($(PackageDir))">
    <!-- Using command line because I want to be sure to get the most up to date list of *.nupkg -->
    <Exec WorkingDirectory="$(BaseDir)"
          Command="move /y *.nupkg &quot;$(PackageDir)&quot;" />
  </Target>
</Project>

I tried using the <Move /> task from MSBuild Community Tasks, but this task uses System.IO.File.Move() to move the files, and this method wont overwrite existing files. I tried <Copy /> followed by <Delete /> tasks, but this did not work out for me either because MSBuild scanned the directory for nupkg files before it had actually built any. The good old DOS move command worked best for me in the end, but you have options.

Test It

If everything is setup right, then you should just be able to switch to “Release” and hit F6. When the build finishes, you should find your package in the PackageDir.

If not, there are several places to look. View the Build Output by selecting “Output” from the “View” menu, and run the build again. Usually this output is enough to find any problems with the build. You should check your AssemblyInfo.cs and make sure you filled out the assembly properties. If NuGet cannot find the tokens it wants for the package, then it might cause the build to fail.

If you want more information, open the command line and run MSBuild by hand:

C:\PizzaStore\PizzaStore\NuGet>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe NuGet.msbuild /p:Configuration=Release /verbosity:d

You can also run MSBuild against your project if you need even more information:

C:\PizzaStore\PizzaStore>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe PizzaStore.csproj /t:Clean,Build /p:Configuration=Release /verbosity:d

One More Thing

The last problem I mentioned was retrieving the version number to use with NuGet on the command line, and I did not show that scenario because it was not necessary for this example. Since we are targeting a project file, NuGet automatically gathers the version number for you. However, if you need to get that information you would use a <GetAssemblyIdentity /> task inside the StdPackage target to get the version, then pass that to NuGet on the command line during the <Exec /> task. Jeremy has an example of the syntax on Technical Jargon, but he uses it with an XmlUpdate task to update the nuspec. Either method should work.

Update 3/2/2012: I posted an example using GetAssemblyIdentity in my post on creating MEF-friendly NuGet packages.