Setting up a common nuget packages folder for all solutions when some projects are included in multiple solutions

NugetNuget PackageSolution

Nuget Problem Overview


I have been using NuGet to retrieve packages from external and internal package sources, which is very convenient. But I have realized that the packages are by default stored per solution, which is very frustrating when some projects with NuGet references are included in several solutions. Then the references are changed to other solutions package folder which may actually be unavailable to another developer or build machine.

I have seen that there are ways to point out a common package location (perhaps at the project root level, we are using TFS source control) with the release 2.1 of NuGet, see release notes . I am using NuGet v2.7

But I have tried to add nuget.config files without seeing any effect of this. Packages are still stored in the solution folder. Is there anything I have missed? There seems to be different structures of the xml node to add to the nuget.config file, depending on who is answering that question: Schwarzie suggests on another Stackoverflow thread:

<settings>
  <repositoryPath>..\..\[relative or absolute path]</repositoryPath>
</settings>

The release notes for NuGet 2.1 (see link above) suggests this format:

<configuration>
  <config>
    <add key="repositoryPath" value="..\..\[relative or absolute path]" />
  </config>
</configuration>

I don't know which one of these, or any, or both will work in the end. I have tried both at solution level. Can the nuget.config file be placed on TFS project root level, or must it be in the solution directory? It seems that NuGet reads and applies the settings from these files in a certain order, why it would make sense to add them in several levels, where a nuget.config file on solution level would override one on the TFS project root level. Can this be clarified?

Do I need to remove all installed packages before those references will work? I would love if someone could provide a step-by-step instruction for moving from solution-specific nuget usage to a common package folder where projects that belong to several solutions can find their required nuget packages.

Nuget Solutions


Solution 1 - Nuget

I have a similar situation with external and internal package sources with projects referenced in more than one solution. I just got this working with one of our code bases today and it seems to be working with the developer workstations and our build server. The below process has this scenario in mind (although it shouldn't be hard to adapt to have the common packages folder else where).

  • Codebase
    • Project A
    • Project B
    • Project C
    • Solutions
      • Solution 1
      • Solution 2
      • Solution 3
      • Packages (this is the common one shared by all solutions)

Updated answer as of NuGet 3.5.0.1484 with Visual Studio 2015 Update 3

This process is a bit easier now than when I originally tackled this and thought it was time to update this. In general, the process is the same just with less steps. The result is a process that solves or provides the following:

  • Everything that needs to be commited to source code control is visible and tracked in the solution
  • Installing new packages or updating packages using the Package Manager in Visual Studio will use the correct repository path
  • After the initial configuration, no hacking of .csproj files
  • No modifications of developer workstation (Code is build ready on check out)

There are some potential downsides to be aware of (I haven't experience them yet, YMMV). See Benol's answer and comments below.

Add NuGet.Config

You will want to create a NuGet.Config file in the root of the \Solutions\ folder. Make sure this is a UTF-8 encoded file that you create, if you are not sure how to do this, use Visual Studio's File->New->File menu and then pick the XML File template. Add to NuGet.Config the following:

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

For the repositoryPath setting, you can specify an absolute path or relative path (recommended) using the $ token. The $ token is based on where the NuGet.Config is located (The $ token is actually relative to one level below the location of the NuGet.Config). So, if I have \Solutions\NuGet.Config and I want \Solutions\Packages I would need to specify $\..\Packages as the value.

Next, you will want to add a Solution Folder to you solution called something like "NuGet" (Right-Click on your solution, Add->New Solution Folder). Solution Folders are virtual folders that only exist in the Visual Studio solution and will not create an actual folder on the drive (and you can reference files from anywhere). Right-Click on your "NuGet" solution folder and then Add->Existing Item and select \Solutions\NuGet.Config.

The reason we are doing this is so that it is visible in the solution and should help with making sure it is properly committed to your source code control. You may want to do this step for each solution in your codebase that is participating with your shared projects.

By placing the NuGet.Config file in \Solutions\ above any .sln files, we are taking advantage of the fact that NuGet will recursively navigate the folder structure upwards from the "current working directory" looking for a NuGet.Config file to use. The "current working directory" means a couple of different things here, one is the execution path of NuGet.exe and the other is the location of the .sln file.

Switching over your packages folder

First, I highly recommend you go through each of your solution folders and delete any \Packages\ folders that exist (you'll need to close Visual Studio first). This makes it easier to see where NuGet is placing your newly configured \Packages\ folder and ensures that any links to wrong \Packages\ folder will fail and can then be fixed.

Open your solution in Visual Studio and kick off a Rebuild All. Ignore all of the build errors you will receive, this is expected at this point. This should kick off the NuGet package restore feature at the start of the build process however. Verify that your \Solutions\Packages\ folder has been created in the spot you want. If it hasn't, review your configuration.

Now, for each project in your solution you will want to:

  1. Right-click on the project and select Unload Project
  2. Right-click on the project and select Edit your-xxx.csproj
  3. Find any references to \packages\ and update them to the new location.
    • Most of these will be <HintPath> references, but not all of them. For example, WebGrease and Microsoft.Bcl.Build will have separate path settings that will need to be updated.
  4. Save the .csproj and then Right-click on the project and select Reload Project

Once all of your .csproj files have been updated, kick off another Rebuild All and you should have no more build errors about missing references. At this point you are done, and now have NuGet configured to use a shared Packages folder.

As of NuGet 2.7.1 (2.7.40906.75) with VStudio 2012

First off the thing to keep in mind is that nuget.config does not control all of the path settings in the nuget package system. This was particularly confusing to figure out. Specifically, the issue is that msbuild and Visual Studio (calling msbuild) do not use the path in nuget.config but rather are overriding it in the nuget.targets file.

Environment Preparation

First, I would go through your solution's folder and remove all \packages\ folders that exist. This will help ensure that all packages are visibly installing into the correct folder and to help discover any bad path references throughout your solutions. Next, I would make sure you have the latest nuget Visual Studio extension installed. I would also make sure you have the latest nuget.exe installed into each solution. Open a command prompt and go into each $(SolutionDir)\ .nuget\ folder and execute the following command:

nuget update -self
Setting common package folder path for NuGet

Open each $(SolutionDir)\ .nuget\NuGet.Config and add the following inside the <configuration> section:

<config>
    <add key="repositorypath" value="$\..\..\..\Packages" />
</config>

Note: You can use an absolute path or a relative path. Keep in mind, if you are using a relative path with $ that it is relative to one level below the location of the NuGet.Config (believe this is a bug).

Setting common package folder path for MSBuild and Visual Studio

Open each $(SolutionDir)\ .nuget\NuGet.targets and modify the following section (note that for non-Windows there is another section below it):

<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
    <!-- Windows specific commands -->
    <NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
    <PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
    <PackagesDir>$([System.IO.Path]::Combine($(SolutionDir), "packages"))</PackagesDir>
</PropertyGroup>

Update PackagesDir to be

<PackagesDir>$([System.IO.Path]::GetFullPath("$(SolutionDir)\..\Packages"))</PackagesDir>

Note: The GetFullPath will resolve our relative path into an absolute path.

Restoring all of the nuget packages into common folder

Open a command prompt and goto each $(SolutionDir)\ .nuget and execute the following command:

nuget restore ..\YourSolution.sln

At this point, you should have a single \packages\ folder in your common location and none within any of your solution folders. If not, then verify your paths.

Fixing project references

Open every .csproj file in a text editor and find any references to \packages and update them to the correct path. Most of these will be <HintPath> references, but not all of them. For example, WebGrease and Microsoft.Bcl.Build will have separate path settings that will need to be updated.

Build your solution

Open your solution in Visual Studio and kick off a build. If it complains about missing packages that need to be restored, don't assume that the package is missing and needs to be restored (error can be misleading). It could be a bad path in one of your .csproj files. Check that first before restoring the package.

Have a build error about missing packages?

If you have already verified that the paths in your .csproj files are correct, then you have two options to try. If this is the result of updating your code from source code control then you can try checking out a clean copy and then building that. This worked for one of our developers and I think there was an artifact in the .suo file or something similar. The other option is to manually force a package restore using the command line in the .nuget folder of the solution in question:

nuget restore ..\YourSolution.sln

Solution 2 - Nuget

Instead of setting common package location for all projects, it is also possible to change HintPath in project as follow:

<HintPath>$(SolutionDir)\packages\EntityFramework.6.1.0\lib\net40\EntityFramework.dll</HintPath>

In most cases there in shared project will be only few packages, so you can easily change it.

I think it is better solution, when you branching code, when setting common repo, you must change relative path, in this solution you don't need to do this.

Solution 3 - Nuget

My experience trying this with the latest version of NuGet (2.7) and VS2012:

  • Delete the .nuget folder (on disk and in solution)
  • Put a NuGet.Config file in a common parent folder of all solutions
  • Delete any existing packages folders
  • Go through all csproj files and change HintPaths to point to the new location
  • Profit

In my case, I wanted to put all packages in .packages, so my NuGet.Config looked like below.

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

Note that there are a few 'strange' things that can happen, but I think they're bearable:

  • If you 'Update' a package from one solution, it will promptly delete the older version from your packages folder (it can't know whether you have another solution which is pointing there). This doesn't bother me greatly, as the other solution will just restore when required.
  • If you try to add package from right-click-on-solution, if the package is already present in another solution, it will see that it's there and show you the 'green tick' instead of the 'install' button. I usually install from right-click-on-project, so this doesn't bother me at all.

Disclaimer: I just tried this today, I don't have any long term experience to back it up!

Solution 4 - Nuget

I have NuGet version 2.8.50926 with VS 2013. You don't need to use multiple nuget.config files, or use complex directory structures. Just modify the default file located here:

%APPDATA%\Roaming\NuGet\NuGet.Config

Here is the content of my file:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <config>
    <add key="repositoryPath" value="C:\Projects\nugetpackages" />
  </config>
  <activePackageSource>
    <add key="nuget.org" value="https://www.nuget.org/api/v2/" />
  </activePackageSource>
</configuration>

So all packages go to the "C:\Projects\nugetpackages" folder, no matter where the solution is.

In all your solutions, just delete existing "packages" folders. Then build your solution, and NuGet will automatically restore the missing packages in the new, centralized directory you specified.

Solution 5 - Nuget

Already there is no need to modify nuget.targets. It has been fixed in nuget 2.8 (http://nuget.codeplex.com/workitem/2921). You only need to set repositorypath.

Solution 6 - Nuget

From Visual Studio 2013 Update 4 and Nugget Package Manager version > 2.8.5...

Create nuget.config file in root of repository.

file content:

<configuration>
  <config>
    <add key="repositoryPath" value="packages" />
  </config>
  <packageSources>
    <add key="nuget.org" value="https://www.nuget.org/api/v2/" />
  </packageSources>
</configuration>

This will cause that all packages will go to packages folder on level of your's nuget.config file.

Now you can go for each .sln nuget console with command 'update-package -reinstall'

If you have like multiple repository at the same level and what to share the same package folder across them try use way to go one folder up.

 <add key="repositoryPath" value="..\packages" />

But this way you cause that nuget packages reference csproj is pointing one folder up outside you repositorys path.

Solution 7 - Nuget

I've concocted a NuGet package that transparently converts all NuGet references in a project to a $(SolutionDir)-relative format. It does so using build-time XSLT transform, so you don't need to hack your project file by hand. You can update your packages freely, it will break nothing.

https://www.nuget.org/packages/NugetRelativeRefs

Or you if you use Visual Studio 2015 Update 3, you can just migrate your package references to project.json form as described here: https://oren.codes/2016/02/08/project-json-all-the-things

Solution 8 - Nuget

Updating my experience with nuget 2.8.3. It was relatively simple. All did was enabled package restore from right clicking solution. Edited NuGet.Config and added these lines :

  <config>
    <add key="repositorypath" value="..\Core\Packages" />
  </config>

Then rebuilt the solution, it downloaded all packages to my desired folder and updated references automatically. I did the same for all of my other projects, where only incremental packages were downloaded and existing packages were referenced. Hence a common package repository for all projects as been set.

Here is a step by step procedure to enable package restore.

http://blogs.4ward.it/enable-nuget-package-restore-in-visual-studio-and-tfs-2012-rc-to-building-windows-8-metro-apps/

Solution 9 - Nuget

Just hardlink /packages to the desired shared location. Then your project won't be broken for other users, that do not have a special packages location.

Open a command prompt as admin and use

mklink /prefix link_path Target_file/folder_path

Solution 10 - Nuget

For any significant solution, the above answers will fall short. Quite simply, a complex TFS workspace structure with differing relative paths, branching, shared projects etc. make a single central repository impossible.

Using ($SolutionDir) is a step in the right direction, but hand coding the csproj file with ($SolutionDir) would become quite tedious in a code base with hundreds of packages that are updated regularly (everytime update occurs, the HintPath gets overwritten with a new relative path). What happens if you have to run Update-Package -Reinstall.

There is a great solution called NugetReferenceHintPathRewrite. It automates the injection of ($SolutionDir) into the HintPaths just prior to build (without actually changing the csproj file). I imagine it could easily be incorporated into automated build systems

Solution 11 - Nuget

A short summary for those on VS 2013 professional with NuGet Version: 2.8.60318.667

This is how you would direct packages to a path relative to the .nuget folder:

<configuration>
  <config>
    <add key="repositoryPath" value="../Dependencies" />
  </config>
</configuration>

For example, if your solution (.sln file) resides in C:\Projects\MySolution, when you enable NuGet package restore, the .nuget folder is created like so: C:\Projects\MySolution.nuget and the packages will be downloaded to a directory like so: C:\Projects\MySolution\Dependencies

NOTE: For some (unknown) reason, everytime I update the "repositoryPath", I have to close and reopen the solution for the changes to take effect

Solution 12 - Nuget

for those using paket as their package manager, there's an auto-symlink option for your dependencies file:

storage: symlink

btw: paket leverages nuget

reference: https://fsprojects.github.io/Paket/dependencies-file.html

If you want to modify as little as possible, you could just clean up subdirectories periodically with a script. ie. Nuget's default behaviour is to copy files from a global location to a local packages folder, so delete these files afterwards.

Solution 13 - Nuget

I just use NTFS junctions to make all the packages folders direct to a single folder above the repository roots. Works great. No problems with parallel package restore across multiple solutions. One advantage to this is you don't have to reconfigure anything at all in your source code, such as the hundreds of relative hint paths in your .csproj files. It 'just works' by letting the file system handle the redirection and simulation of a single packages folder.

Just watch out for 'git' issues. Although 'git status' via command line shows no unstaged changes, I noticed that GitKraken sees 'package' junction as an unstaged file. It even shows errors like 'file is a directory' when you click it. GitKraken will also try to stash this 'file' if you rebase, destroying the junction, and restoring it as an actual file that contains text with the original file path. Very strange behavior. Maybe be able to work around it by ensuring packages file is added to your .gitignore.

Solution 14 - Nuget

These are the instructions as of NuGet 2.1: http://docs.nuget.org/docs/release-notes/nuget-2.1

No need to edit solution level files.

Works with Visual Studio 2013 and three solutions sharing projects.

Don't forget to update solution level NuGet (each nuget.exe in .nuget folders).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMats IsakssonView Question on Stackoverflow
Solution 1 - NugetVermisView Answer on Stackoverflow
Solution 2 - NugetAdam WyżgołView Answer on Stackoverflow
Solution 3 - NugetBenjolView Answer on Stackoverflow
Solution 4 - NugetMatthieuView Answer on Stackoverflow
Solution 5 - NugetdanielView Answer on Stackoverflow
Solution 6 - NugetJanusz NowakView Answer on Stackoverflow
Solution 7 - NugetOleg TarasovView Answer on Stackoverflow
Solution 8 - Nugetamarnath chatterjeeView Answer on Stackoverflow
Solution 9 - Nugetuser803469View Answer on Stackoverflow
Solution 10 - NugetgravidThoughtsView Answer on Stackoverflow
Solution 11 - NugetSudhanshu MishraView Answer on Stackoverflow
Solution 12 - NugetsgtzView Answer on Stackoverflow
Solution 13 - NugetTriynkoView Answer on Stackoverflow
Solution 14 - Nugetuser3285954View Answer on Stackoverflow