NuGet, MsBuild, and Import packages

May 9, 2011 at 5:57 AM

I would like to be able to use NuGet to import MSBuild tasks from a repository rather than requiring them to be installed on the machine.  Ideally they could be stored in the solution's packages folder rather than in Program Files.  In other words, instead of:

<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks"/>

or (and I'm not sure that this kind of scenario would work) instead of referencing the package in the project's packages.config and include it by its relative location:

<Import Project="$(SolutionPath)\packages\MSBuild.ExtensionPack.4.0\MSBuild.ExtensionPack.tasks"/>

Something more along these lines:

<Import Package="MSBuild.ExtensionPack" Version="4.0" />

The packages.config solution might look better at first glance because it could work without making changes to msbuild.  However, it would break the build for people who don't have the package installed locally at build-time.  My preferred usage of NuGet is to not check the packages in to source control, but to change the BeforeBuild target to install missing packages via the nuget commandline (as referenced in David Ebbo's blog post).  The Package/Version syntax I'm proposing would change the behavior of msbuild a little bit to support this.  If msbuild encountered <Import Package="MSBuild.ExtensionPack" Version="4.0" />, it would check to see if $(SolutionPath)\packages\MSBuild.ExtensionPack.4.0\MSBuild.ExtensionPack.tasks exists implicitly and download it if it does not... in one atomic step, without raising an error for the current build.  It would eliminate the need to manually install msbuild extensions on every developer & continuous integration/build agent machine and it would give developers some insight into when their projects are referencing old/buggy versions of build tasks.

If you coupled the feature with some capabilities or guidance for editing project files as a part of NuGet package installation (i.e., a PowerShell script that uses the msbuild API to modify the .csproj or .csproj transformations?), it would open up the possibility of creating packages that improve and alter the build process of a project similar to how the MvcScaffolding package manipulates the contents of a project.  The ability for a NuGet package developer to add a dependency to a package that extends the capabilites of msbuild would enable some very powerful scenarios.  

Here is an example that I have been working on:

I'm currently calling SubWCRev.exe as a part of my build process to perform variable substitution against AssemblyInfo.cs with context about the state of the SVN working copy.  I have an HttpHandler that outputs some diagnostic information derived from the AssemblyInfo (via Reflection) about the state of all the libraries referenced by the application and an HttpModule that throws an exception in Application_Start when code built in Release mode is out of date/doesn't correspond to a tag in SVN.  The subwcrev.exe process is ugly and could probably be better handled by an MSBuild task that deals with Subversion directly.  Plus the Module & Handler can't be easily distributed at this point as a NuGet package since they intimately depend on the build-time tasks and the installation of a subversion client with subwcrev somewhere in the path.  I'd like to be able to package & share this capability, but it would be annoying and not very NuGetty to have the manual prerequisite steps.