A less terrible .NET project build with NuGet

nuget 12 comments suggest edit

According to Maarten Balliauw, Building .NET projects is a world of pain. He should know, he is a co-founder of MyGet.org which provides private NuGet feeds along with build services for those packages.

He’s also a co-author of the Pro NuGet book, though I might argue he’s most famous for his contribution to Let Me Bing That For You.

His post gives voice to a frustration I’ve long had. For example, if you want to build a project library that targets Windows 8 RT, you have to install Visual Studio on your build machine. That’s just silly fries! (By the way, if you have a solution that doesn’t require Visual Studio, I’d love to hear it!)

UPDATE: Nick Berardi writes about an approach that doesn’t require Visual Studio. Of course, there are several caveats with that approach. First, any upgrade requires you re-do the copy. Second, I’m not sure what the licensing implications are. You might still technically need a Visual Studio license for that server to do this. In any case, I opened a User Voice issue asking Microsoft to just clean this mess up and make it easier for us to do this.

Maarten doesn’t just rant about this situation, he proposes a solution (emphasis mine):

I do not think we can solve this quickly and change history. But I do think from now on we have to start building SDK’s differently. Most projects only require an MSBuild .targets file and some assemblies, either containing MSBuild tasks or reference assemblies, to do their compilation work. What if… we shipped the minimum files required to succesfully build a project as NuGet packages?

This philosophy aligns well with my personal philosophy on self-contained builds and was a key design goal with NuGet. One of the guiding principles I wrote about when we first announced NuGet:

Works with your source code. This is an important principle which serves to meet two goals: The changes that NuGet makes can be committed to source control and the changes that NuGet makes can be x-copy deployed. This allows you to install a set of packages and commit the changes so that when your co-worker gets latest, her development environment is in the same state as yours. This is why NuGet packages do not install assemblies into the GAC as that would make it difficult to meet these two goals. NuGet doesn’t touch anything outside of your solution folder. It doesn’t install programs onto your computer. It doesn’t install extensions into Visual studio. It leaves those tasks to other package managers such as the Visual Studio Extension manager and the Web Platform Installer.

There’s a caveat that NuGet does store packages in a machine specific location outside of the solution, but that’s an optimization. The point is, a developer should ideally be able to checkout your code from GitHub or other source hosting repository and build the solution. Bam! Done! If there’s too many more steps than that, it’s a pain to contribute.

Fortunately, there are some great features in NuGet that can help package authors reach this goal!

Import MSBuild targets and props files into project

NuGet 2.5 introduces the ability to import MSBuild targets and prop files into a project. As more projects take advantage of this feature, we’ll hopefully see the demise of required MSIs in order to work on a project. As Maarten points out, MSIs (or Visual Studio Extensions) are still valuable in order to add extra tooling. But they shouldn’t be required in order to build a project.

Development-only dependencies

In tandem with importing MSBuild targets, NuGet 2.7 adds the ability to specify development-only dependencies.

This feature was contributed by Adam Ralph and it allows package authors to declare dependencies that were only used at development time and don’t require package dependencies. By adding a developmentDependency="true" attribute to a package in packages.config, nuget.exe pack will no longer include that package as a dependency.

These are packages that do not get deployed with your application. These packages might include MSBuild targets, code contract assemblies, or source code only packages.

You can see an example of this in use with Octokit.net in its packages.config.

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="DocPlagiarizer" version="0.1.1" targetFramework="net45" developmentDependency="true" />
  <package id="SimpleJson" version="0.34.0" targetFramework="net45" developmentDependency="true" />
</packages>

My recommendation to package authors is to consider a separate *.Runtime package that contains just the assemblies that need to be deployed and a separate main project that depends on that package that brings in all the build-time dependencies such as MSBuild targets and whatnot. It keeps a nice separation and works well for other non-Visual Studio NuGet consumers such as Web Matrix, ASP.NET Web Pages, Xamarin, etc.

At the end of his post, Maarten notes that there is good progress towards build sanity.

P.S.: A lot of the new packages like ASP.NET MVC and WebApi, the OData packages and such are being shipped as NuGet packages which is awesome. The ones that I am missing are those that require additional build targets that are typically shipped in SDK’s. Examples are the Windows Azure SDK, database tools and targets, … I would like those to come aboard the NuGet train and ship their Visual Studio tooling separately from teh artifacts required to run a build.

This reminds me of a feature proposal I wrote a draft specification for a long time ago called Related Dependencies. You can tell it’s old because it refers to the old name for NuGet.

These are basically “optional” dependencies that can bring in tooling from other package managers such as the Visual Studio Extensions gallery. In the spec, I mentioned “prompting” but the goal would be a non-obtrusive way for packages to highlight other tooling related to the package dependency and make it easy for developers to easily install all of them.

In my mind, this would be similar to how you are notified of updates in the Visual Studio Extension Manager (now called “Extensions and Updates” dialog). Perhaps there’s another tab that lets you see extensions related to the packages installed in your solution and an easy way to install them all.

But these would have to be optional. You should be able to build the solution without them. Installing them just makes the development experience a bit better.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

12 responses

  1. Avatar for Paul Cox
    Paul Cox April 16th, 2014

    You also have to consider the MSBuild dependency which unfortunately then implies an OS dependency as we found out to our detriment recently.

    We found we could no longer build the product on our build server after upgrading to MVC 5 as our build server runs Windows Server 2008 and MSBuild now requires at least 2008 R2.

    Maybe MS could open source MSBuild now that it is separate from the VS release cycle. Then you could have a cross platform build tool specified as a NuGet dependency on your project that is automatically restored during the build.

  2. Avatar for Kristian Hellang
    Kristian Hellang April 16th, 2014

    YES! Optional dependencies makes so much sense. Not only for tooling from other package managers, but for other NuGet packages as well.

    Some examples where this would be very handy are scriptcs, Glimpse and Nancy. All have a core library, but the core doesn't really provide much value (except Nancy which has default implementations included) without engines, adapters, serializers etc. that depend on the core module and actually does the work.

    It would be awesome to have a way to just "Install-Package scriptcs" and get prompted to select related packages, e.g. "Do you want the Roslyn or the Mono engine?".

  3. Avatar for Xavier Decoster
    Xavier Decoster April 16th, 2014

    A big +1 on this topic!! Not sure about WinRT "apps", but for WinRT libs, you can copy the contents of C:\Program Files (x86)\MSBuild\Microsoft\WindowsXaml\v12.0 and \v11.0 to your build server to resolve the missing references. The madness! :-)

  4. Avatar for adamralph
    adamralph April 16th, 2014

    now that *would* be sexy

  5. Avatar for adamralph
    adamralph April 16th, 2014

    I'm so glad to hear others are feeling my frustrations too. Hopefully that means we can collectively get something done about it.

    Code analysis is another broken mess. In my team we've managed to jump through several hoops and get v 11.0 code analysis working on a build agent but we had to give up with v 12.0.

  6. Avatar for adamralph
    adamralph April 16th, 2014

    Good post Phil, some good advice there.

    I have an extra snippet of info for package authors: if you are authoring a package which is likely to be installed as a development dependency in almost all cases, starting with NuGet 2.8 you can add <developmentdependency>true</developmentdependency> to your nuspec metadata. This will inform NuGet to add developmentDependency="true" in packages.config when developers install your package.

    Of course, a developer can still remove the attribute if they need to.

  7. Avatar for marylove
    marylove April 18th, 2014

    I'm so glad to hear others are feeling my frustrations too. Hopefully that means we can collectively get something done about it

    https://sites.google.com/si...

  8. Avatar for Rudi Larno
    Rudi Larno April 18th, 2014

    Might I suggest to vote on uservoice: http://visualstudio.uservoi...

  9. Avatar for lovely
    lovely April 21st, 2014

    I'm so glad to hear others are feeling my frustrations too. Hopefully that means we can collectively get something done about it.

    http://chatzalo.org/

  10. Avatar for Warren
    Warren March 6th, 2015

    Wow... that's some good info written on NuGet. Need to look into it further myself. http://www.filmrally.com/

  11. Avatar for cristal
    cristal March 8th, 2015

    It would be great if we had a way for ASP.NET apps to register scheduled tasks outside of the web app domain but sandboxed to only access their parent application. Kind of a safe way to allow a medium trust web app to add a windows scheduled task. Just brainstorming...

  12. Avatar for Edwin Quai Hoi
    Edwin Quai Hoi September 2nd, 2016

    Coming from a java world where build and dependency management is mature..it was a shock trying to implement devops/ continuous integration on a .net platform. In my opinion .net needs to follow the maven way where build and dependency management are first class citizens along with a bunch of conventions and archtetypes to build a typical .net artifact