Improved Package Restore

As we’ve previously explained we receive various reports on how our NuGet packages don’t play nicely with NuGet’s package restore feature. In this blog post I’ll talk about an update we shipped to our infrastructure package Microsoft.Bcl.Build that reduces the impact.

The Issue

To recap, some of our packages need to participate in the build in order to work properly. Examples of tasks that require build participation include generating binding redirects or selecting the appropriate native dependency based on the CPU architecture the build is producing.

Build participation is implemented by adding an import to an MSBuild .targets file. Generally, build participation isn’t optional which is why we decided not to use NuGet’s feature to add a target file import. NuGet will add the import with a condition that checks whether the .targets file already exists, i.e. whether the NuGet package was already restored.

Both solutions aren’t ideal:

  • A non-optional import ensures that building will fail when packages are missing because the imported target file can’t be found. However, this also means that you can’t open the solution in Visual Studio (see picture below). Also, the error message isn’t really actionable as it doesn’t include any instructions on how to fix the issue.

  • An optional import allows the project to load and build. However, even if you have package restore enabled, the first build may not work because by the time the project was build, the targets file was still missing. This can result in build breaks or – even worse – the build may succeed but with incorrect outputs.

The imported project “<solution-path>\packages\Microsoft.Bcl.Build.1.0.8\tools\Microsoft.Bcl.Build.targets” was not found. Confirm that the path in the declaration is correct, and that the file exists on disk.

The Improvement

We’ve updated Microsoft.Bcl.Build to use a different approach. The new version will use a conditional import similar to what NuGet’s automatic import feature does. This will always allow the project to load in Visual Studio.

However, Microsoft.Bcl.Build also adds a target to your project that will run after the build is finished. This target checks whether the current build restored packages and if so fail the build with an actionable error message:

The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568.

Building a second time will fix this error. Please note that this error will only appear if packages were missing so it’s not like you always have to build twice.

This solution doesn’t address build server / continuous integration (CI) scenarios. In order to successfully use package restore on the build server, you have two options:

  1. Check-in the .targets file.
  2. Explicitly run NuGet package restore prior to building your project/solution.

NuGet 2.7

Prior to version 2.7, NuGet makes package restore a bit more complicated than most people would like it to be:

  1. First of all you need to manually enable it for the solution. This will add a NuGet.exe, NuGet.config and NuGet.targets file to your solution and you are expected to check those files in.
  2. Explicitly running package restore for the solution isn’t a single step. You need to run a command for each project, such as nuget.exe install .\Project1\packages.config.

NuGet 2.7 makes this a lot easier:

  1. You no longer need to enable package restore explicitly – when building in VS all packages are restored automatically.
  2. Running package restore on a build machine is now super easy. You only need to check-in NuGet.exe (nothing else) and you can put it wherever you want. It can even be in a well-known location on your build server and excluded from every solution if desired. Prior to the build you simply run nuget.exe restore path\to\my\solution.sln.

Also, the NuGet team is talking to all major providers of build/CI servers (incl. TFS) so that at some point the second step can be handled automatically by the build servers. For more details, have a look at the NuGet 2.7 release notes and the new Package Restore documentation.

Summary

The new version of Microsoft.Bcl.Build will ensure that solutions containing our packages will load successfully even if packages aren’t restored yet. This affects all .NET NuGet packages that depend on it, such as Microsoft.Net.Http, Microsoft.Bcl, and Microsoft.Bcl.Async.

Microsoft.Bcl.Build will give an actionable error message in cases the package was missing by asking you to build again.

When coupled with NuGet 2.7 where package restore is automatic in Visual Studio and isn’t implemented through MSBuild, the experience is transparent and smooth. However, this doesn’t address build server scenarios yet so you still need run nuget.exe restore solution.sln prior to build, or check-in the .targets file if preferred.