Configuring Common NuGet Repository for Multiple Solutions

December 23rd 2013 NuGet Visual Studio

When using NuGet, you typically don't need to worry about its repository location. In simple every day scenarios it just works without even paying attention to it. If you've ever used package restore functionality, you might have noticed that the packages are actually downloaded to the packages folder alongside the solution file because you had to exclude it from source control, but that's probably the most thought you have ever given to it.

Once you have more than a single solution file for your product, this default approach starts to break down. To be more precise; the problems occur when a single project file is included in multiple solutions files, which are not placed in the same folder. Since by default the repository is placed in the same folder as the solution file, there will be two repositories in this case – one for each solution file. Depending on the opened solution, the packages for the shared project will be placed in either one of the repositories when added to a project. Let's demonstrate this on a simple example:

  • Root
    • Common
      • ProjectA
        • ProjectA.csproj
    • Solution1
      • Solution1.sln
      • ProjectB
        • ProjectB.csproj
    • Solution2
      • Solution2.sln
      • ProjectC
        • ProjectC.csproj

Let's assume that ProjectA is included in both Solution1 and Solution2. Its references will by default point to:

  • ..\..\Solution1\packages when added from Solution1, or
  • ..\..\Solution2\packages when added from Solution2.

At first everything will work fine, but as soon as a different developer will retrieve the code from source control, who doesn't yet have the packages on his disk, the build will fail even after restoring the packages. The package restore process will put all the packages in the repository of the currently opened solution, but the reference paths will point to the other repository if they were added from that second solution. A possible workaround would be, to restore the packages for both solutions before building; but there is a cleaner way to solve the problem.

The default repository location can be changed using a configuration file named NuGet.config. Because of the way NuGet is searching for this file and reading its contents, it's easy to create a single configuration file for all the solutions in a product and share it with other developers using source control: after checking the machine specific and user specific locations, NuGet will start traversing the folders from the drive root up to its working directory and use the setting value from the last configuration file it encounters, i.e. the one closest to the working directory.

The issue from the above example can therefore be solved by creating a NuGet.config file with the following contents in the Root folder and include it in source control:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <config>
    <add key="repositorypath" value=".\packages" />
  </config>
</configuration>

Now both solutions will use the same repository (i.e. Root\packages) and the problem of missing packages after package restore will be gone. Of course you need to set this up before adding any packages to your projects, otherwise you'll need to:

  • either remove all of the packages and add them again (the recommended way),
  • or manually fix the paths of the referenced assemblies from the packages in the project files (make sure you know exactly what you're doing).

You can change the repositorypath value in the configuration file to your liking, just make sure it is defined relative to its location. Here's why I think other approaches don't work well, even if they seem a good idea at first:

  • Absolute paths will force all developers to have the repository at the same location which is not only impractical but can also be impossible when they don't have a disk with that letter or don't have enough available space or required permissions on it. Also, you really don't want the referenced assemblies in your projects to point to a fixed path.
  • Leaving up the path to the developers (by setting it in a machine or user specific configuration file) won't work for the same reason that the default location doesn't work: project files contain fixed paths relative to the project file location and are the same for all developers.

You should also make sure to you use automatic package restore that is available since NuGet 2.7 instead of its older alternative: MSBuild-integrated package restore. For it to work and be used, you need your developers to have at least NuGet 2.7 installed and you must not execute the Enable NuGet Package Restore command from Visual Studio. Otherwise your project files will include a fixed reference to the NuGet.targets file placed below the current solution file, causing a similar problem to the original one.

Automatic package restore works without NuGet.exe and NuGet.targets included in source control and solves other issues of MSBuild-integrated package restore as well (e.g. projects failing to load or building incorrectly before the packages are restored if they reference a .targets file from a package). From Visual Studio this new approach to package restore works without having to take any additional actions. If you want to build your projects with a build script as well (a common scenario on a build server), make sure you have an updated version of NuGet.exe in the path and call the following command for each solution before building it:

nuget.exe restore MySolution.sln

If you've found this blog post useful, you might also be interested in the NuGet book that I coauthored and has just recently been released: NuGet 2 Essentials. Until January 3rd you can buy it directly from the publisher in eBook format for only 5 USD.

Copyright
Creative Commons License