Packaging Source Code With NuGet

• Tracking Real World Web Performance
• WebPageTest Deep Dive
.NET developers depend on binaries a lot. Assemblies and executables are our typical unit of deployment and consumption and NuGet itself is basically a sophisticated binary distribution system.
I don’t think there is anything necessarily wrong with that. Assemblies offer a lot of power and have plenty of benefits, but a few times over the last year or so I’ve wondered if our assembly usage follows the law of the instrument.
I suppose it is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail. – Abraham Maslow
Plenty of other development communities very happily exist in a world without assemblies or binaries. The Ruby gems and npm repositories are absolutely filled to the brim with packages that contain no binary files whatsoever. What do they contain? Source code.
Recently I’ve been wanting to create a NuGet package that would contain common AutoFixture configuration for intrinsic ASP.NET types that I find myself repeatedly using. Unfortunately, every other user of the package would want to slightly tweak the configuration for their project, so I figured I’d simply ship the package with only the source code (no pre-compiled assembly) so that they could easily do that.
When I told Louis DeJardin about this idea earlier in the year at MonkeySpace, his eyes lit up and he got excited. It turns out he’s been thinking about source only packages quite a bit and has even released several OWIN packages as source only.
Louis told me about a few best practices and conventions that he has found useful when creating source only packages. I’ve noticed that only Damian Hickey has picked up on these, so with Louis’ permission, I’ve decided to publish them here for greater exposure:
NuGet Source-Only Package Conventions and Practices
- Package ID’s should end in
.Sources
, which aligns well with and builds upon the.Sample
suffix recommended in the NuGet Package Conventions documentation. - In the
.nupkg
itself, source code files should be placed in a directory with the pathcontent/App_Packages/{package-id}.{package-version}
. Similar to how the.Sources
suffix builds upon the prior art of.Samples
, theApp_Packages
directory follows theApp_Start
folder nomenclature used by David Ebbo’s popular WebActivator package.Here’s an example of a package following this convention, as seen in NuGet Package Explorer:
This convention also allows for a very convenient upgrade path which I’ll cover later on. - Source-Only packages can depend on any other package, including other Source-Only packages.
- Source-Only packages may leverage NuGet’s Source Code Transformations (
*.pp
files) to inject project properties into the source. This is most often seen with the use of the$rootNamespace$
property, but any project property can be used. - In some situations, it may be useful to also ship a standard “binary” package in addition to the Source-Only package.
- Types in Source-Only packages should be marked as internal by default in order to not pollute the target code’s public API.
- Consider using conditional compilation symbols or partial classes in Source-Only packages to provide flexibility and the ability to customize the source to users.
Examples of this technique include allowing for multiple platform targeting options and changing the accessibility of types to public when desired. SimpleJson has a few good examples of this in their source code.
When to Create Source-Only Packages
There are plenty of situations where a Source-Only package does not make sense. Here’s a few things to consider:
- DO consider creating a Source-Only package for “utility” libraries that feature heavy usage of static and/or extension methods. Examples of these types of utility libraries include unit test assertion libraries (see note below!) and the popular DataAnnotationsExtensions package.
- DO consider creating a Source-Only package for small single purpose libraries. SimpleJson is already doing this (though not following these conventions) but you can image any code appropriate for a blog post or Gist would fit the definition well. (Aside: a Gist to Source-Only NuGet Package service would be pretty useful!)
- DO consider creating a Source-Only package for common configuration and setup code or any code which will require tweaking by the user.
- DO NOT consider creating a Source-Only package as a means to simply make step-debugging easier. Instead leverage a symbols package.
Source-Only Package Update Process
One of the nice things about assemblies is that the process of versioning them is well understood. How do we version and update source-only NuGet packages? Luckily, NuGet’s native handling of content works in our favor. Let’s explore with an example:
I’ve created a source-only package called Reverse.Sources
that contains an extension method for reversing a string. Let’s install it:
Install-Package Reverse.Sources -Version 1.0.0
Great, now our project contains that StringExtensions.cs
file with an implementation to reverse strings. It compiles right along with our application and is relatively out of our way.
Unfortunately, version 1.0.0 of my package had a bug and blows up if a null string is passed into the Reverse method. I’ve added a simple guard clause to fix the problem and released it in version 1.1.0. Let’s update:
Update-Package Reverse.Sources
Notice Solution Explorer looks nearly identical – Reverse.Sources.1.0.0 was simply replaced, along with all of it’s files, by Reverse.Sources.1.1.0. I’ve updated my project without any troubles and I have that nice bug fix now.
But what if we had made changes to StringExtensions.cs?
NuGet would have simply left behind the files you’ve edited.
We’d know that there was a problem too because the compiler would complain with a “The namespace ‘Sources_Example’ already contains a definition for ‘StringExtensions’” error.
To fix that error we can use a text diff/merge tool to move the changes over and delete the old 1.0.0 folder.
To me this is a pretty clean upgrade process. Sure we could sometimes get into a situation where we have to deal with merging, but we also get the benefits of working directly with the code.
Are Source-Only Packages A Bad Idea?
Perhaps yes, perhaps no. I don’t think they are that crazy of an idea though. Large numbers of developers outside the .NET ecosystem already work almost exclusively with source only packages. Further, I’d propose that you do as well if you’ve ever included jQuery, Modernizr or any other JavaScript NuGet package in your project.
I for one probably wouldn’t want all packages to be source-only (we certainly won’t be shipping Glimpse like this!), but there are situations where I think it could be extremely useful – particularly in scenarios involving extensibility/tweaking and reducing dependency overhead when I might have reached for ILMerge otherwise.
I’m hoping that this post can start a bit of a conversation about the idea of source only packages and increase the community’s comfort with them. I’m interested to hear the thoughts of the NuGet team and will be submitting these conventions to the NuGet documentation shortly.
Note: It looks like this is on Brad Wilson’s mind too!
Part of me wonders if @xunit v2 should ship Assert as NuGet-based source instead of binary, so you can easily extend it (or ignore it).
— Brad WARLOCKson (@bradwilson) September 1, 2013
Updated Oct 25th with feedback from Phil Haack, Louis DeJardin, Prabir Shrestha and Andrew Nurse. Thanks for the feedback guys!