http://blogs.clariusconsulting.net/kzu

Daniel Cazzulino's Blog

Go Back to
kzu′s Latest post

How to create lightweight reusable source code with NuGet

Sometimes, you just have a reusable helper that you bring in to every project (i.e. an argument validation thingy, or the static reflection stuff, useful extension methods, and so on). You don’t always need them all, they are generally single-file utilities, and having a Common.dll looks overkill or dumb.

Loose source file sharing has always been problematic, though: no packaging and versioning mechanism (unlike an assembly), hard to integrate with source control systems (SVN has external repository mappings, but nothing like that exists in TFS or Mercurial, AFAIK), and so on. I tried source files reuse before, with somewhat poor results.

In a recent project I wanted to reuse (again) that darn useful Guard.cs file. But I set to do it differently this time, and see if my "dream" for lightweight loose source file reuse could still be achieved.

Two key technologies enable some pretty cool and robust source file reuse nowadays:

  • NuGet: unless you’ve been living under a rock, you probably heard all the bignamestalking about it. I’ll save the introductions. But for our particular case, it’s the perfect mechanism to deploy simple "content" files to the user’s project. When you "install" the loose source file package, you essentially get the file added automatically to the project. When you "uninstall" it, the file gets removed. Clean, x-copy.
  • Mercurial: versioning of loose files can be a bit of nightmare. If everyone has a copy of the file, how do we evolve the helpers, how to we build a community around it? Mercurial and in particular CodePlex implementation, allows dead-easy forking, updating and contributing back (sending "pull requests") to the original author.

So let’s get started and create the Guard package

Creating the reusable source file

I just want to be able to install the Guard package and get the following simple file in my project:

using System;
using System.Diagnostics;
using System.Linq.Expressions;

///
/// Common guard class for argument validation.
///
[DebuggerStepThrough]
internal static class Guard
{
    ///
    /// Ensures the given  is not null.
    /// Throws  otherwise.
    ///
    public static void NotNull(Expression> reference, T value)
    {
        if (value == null)
            throw new ArgumentNullException(GetParameterName(reference), "Parameter cannot be null.");
    }

    ///
    /// Ensures the given string  is not null or empty.
    /// Throws  in the first case, or
    ///  in the latter.
    ///
    public static void NotNullOrEmpty(Expression> reference, string value)
    {
        NotNull(reference, value);
        if (value.Length == 0)
            throw new ArgumentException(GetParameterName(reference), "Parameter cannot be empty.");
    }

    private static string GetParameterName(Expression reference)
    {
        var lambda = reference as LambdaExpression;
        var member = lambda.Body as MemberExpression;

        return member.Member.Name;
    }
}

Simple enough. Note the class is internal, as it’s intended to be added to the project where it will be used.

Creating the NuGet package

There are two pieces to creating a package:

To streamline the second part, I created a NuGet folder under the mercurial code repository, with the following structure:

\NuGet
    \content
        Guard.cs
    Guard.nuspec

That’s all we need for the first cut.

The nuspec file is a very simple to author XML file, mine looking as follows:

<package>
        <metadata>
                <id>Guard</id>
                <version>1.0.0.0</version>
                <description>The only argument validation file you need, with full refactoring support and strong-typing.

Examples:
  Guard.NotNull(() =&gt; value, value)
  Guard.NotNullOrEmpty(
    () =&gt; stringValue, stringValue)
        </description>
                <authors>
                        <author>Daniel Cazzulino</author>
                </authors>
                <language>en-US</language>
                <projecturl>http://guard.codeplex.com</projecturl>
                <iconurl>http://www.codeplex.com/Project/Download/FileDownload.aspx?ProjectName=guard&amp;DownloadId=177040</iconurl>
                <licenseurl>http://guard.codeplex.com/license</licenseurl>
        </metadata>
</package>

Nothing out of the ordinary there.

And, believe it or not, that’s all we need.

Testing the package

In order to test the package, we need to download the NuGet Command Line Tool. I put it outside of the NuGet folder I created above. Now I just need to run the following command to generate the actual package:

NuGet.exe pack NuGet\Guard.nuspec -BasePath NuGet

Note that I use the BasePath option so that all the NuGet conventions kick in from the right place, automatically embedding the content folder, which is used for files that are added as-is to the project where the package is installed.

After running the command, we just need to add the path to it via Tools | Options | Package Manager:

image

Now we can just create a test project (ConsoleApplication23 or whatever) and click View | Other Windows | Package Manager Console. The console window has a dropdown that will show the names we saw in the above dialog, and once we pick our Guard package "repository", we can issue a Get-Package command to list what’s there:

image

And if we now type:

PM> Install-Package Guard

sure enough, we get the Guard.cs file in our project! yay!

image

Note that there’s also a NuGet configuration file that allows it to track what packages have been installed on that project.

And typing:

PM> Uninstall-Package Guard

removes it.

Publishing the package

Currently, there’s a temporary repository provided by the NuGet team at http://nupackpackages.codeplex.com/. The instructions in that main page are very straight forward and easy to follow. Essentially:

  • Sign-in with to CodePlex.com, open the Source Code page for NuGet Packages and click the Create a fork under Code Contributions on the right.

  • If you have TortoiseHg installed, you can just right-click on any folder, select Clone, and enter the clone Url you got in the previous step.

  • Wait for all packages in the world to download to your machine Winking smile
  • Add the contents of our NuGet folder (the one with Guard.nuspec) to the PackageSources we just downloaded, under Guard\1.0.0.0

  • Commit those newly added files, and push them to the server.

  • Back in CodePlex, go to the Forks tab, and click the Send Pull Request for your fork.

That’s it. We just need to wait for the cool guys in the NuGet team to merge our package definition.

Register at the http://nuget.org site and start publishing your packages!

Deploying code snippets with our package

In order to add a little usability twist to our loose source file reuse, we will add two code snippet to our package. notnull and notempty. The former for reference types, and the latter specifically for strings that must not be empty or null. So we’ll enable this code snippet experience for our users:

image

A walkthrough on how to create code snippets is out of scope for this blog, but it’s an easy task. Here’s the notnull one:

<!--
    Template for a Guard.NotNull method call.
    Press notnull[TAB][TAB] to get it.
-->
<codesnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
        <codesnippet format="1.0.0">
                <header>
                        <title>Guard null argument</title>
                        <shortcut>notnull</shortcut>
                        <description>Call Guard.NotNull for the given argument</description>
                        <author>Daniel Cazzulino</author>
                        <snippettypes>
                                <snippettype>Expansion</snippettype>
                        </snippettypes>
                </header>
                <snippet>
                        <declarations>
                                <literal>
                                        <id>argument</id>
                                        <tooltip>Name of the argument to guard</tooltip>
                                        <default>value</default>
                                </literal>
                        </declarations>
                        <code language="csharp"><![CDATA[Guard.NotNull(() => $argument$, $argument$);
            $end$]]></code>
                </snippet>
        </codesnippet>
</codesnippets>

The other one is pretty much the same.

Stuff that is not assembly references neither source files go into the tools folder for the package. In our case, we will need to copy the two snippet files over to the user’s profile directory, under Visual Studio 2010\Code Snippets\Visual C#\My Code Snippets. The place to hook this kind of install (copy) and uninstall (delete) automation is a couple of powershell scripts, named install.ps1 and uninstall.ps1.

So under our Guard source control root folder, we end up with:

\NuGet
    \content
        Guard.cs
    \tools
        install.ps1
        uninstall.ps1
        notempty.snippet
        notnull.snippet
    Guard.nuspec

The install.ps1 file contains:

param($installPath, $toolsPath, $package, $project)
  copy-item $toolsPath\*.snippet -destination ([System.Environment]::ExpandEnvironmentVariables("%VisualStudioDir%\Code Snippets\Visual C#\My Code Snippets\"))

The received parameters are self-explanatory, except for $project, which is the EnvDTE.Project where the package is being installed. Here I’m using the VS-only environment variable %VisualStudioDir%, which points to the user profile location used by VS.

and uninstall.ps1:

param($installPath, $toolsPath, $package, $project)
  remove-item ([System.Environment]::ExpandEnvironmentVariables("%VisualStudioDir%\Code Snippets\Visual C#\My Code Snippets\notnull.snippet"))
  remove-item ([System.Environment]::ExpandEnvironmentVariables("%VisualStudioDir%\Code Snippets\Visual C#\My Code Snippets\notempty.snippet"))

Now that’s it! Just re-building the package is enough to get the changes in, and sure enough, our code snippets will get deployed and removed as necessary.

Comments

7 Comments

  1. Thanks for these explanations

  2. [...] followed the lead of people like Daniel Cazzulino and Rob Conery, and made the package a “loose file” install. Instead of adding a [...]

  3. Useful library – any chance you could release this under a more permissive license like Boost? I don’t want to have to include a BSD license file with every executable I distribute simply because it references a 140-line utility class. Also, I’m starting to get the feeling that I’m the only commercial software developer who actually reads open source licenses.

    http://www.boost.org/users/license.html

  4. BSD is very liberal and permissive. I don’t understand how Boost improves upon it. It almost seems like the same text ;)

  5. My understanding is that BSD/MIT require you to include the license file along with the code even if it is distributed purely in object form (i.e. compiled into an EXE). That is my reading of them, and is ostensibly the reason why my flat screen television includes a user interface to view all the open source licenses associate with the libraries it is using. Boost includes an explicit exception allowing you to omit the license when “copies or derivative
    works are solely in the form of machine-executable object code generated by
    a source language processor.”

  6. Anyway, it’s just an annoyance I have with these OSS licenses in general. Including the license along with object code seems like an unnecessary requirement.