ChatGPT解决这个技术问题 Extra ChatGPT

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

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.

I suspect that the short answer to your question (hidden in Vermis' answer below) is that you were missing the $ in front of the relative path. Also, the answer to your question about NuGet.Config files is here. It first looks in .nuget, then in all parent directories, then at the 'global' file in your AppData: then applies them in REVERSE order (whatever that means).
This seems to be hard. There is a tool called Paket which could be solution to this problem: fsprojects.github.io/Paket
Late comment. Just wanted to add that Visual Studio needs to be restarted if you have it running when you start this change, since nuget.config files seem to be read during VS startup. Also, I had no problem without the $, just don't start with a backslash.

C
Community

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)

Project A

Project B

Project C

Solutions Solution 1 Solution 2 Solution 3 Packages (this is the common one shared by all 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:

Right-click on the project and select Unload Project Right-click on the project and select Edit your-xxx.csproj Find any references to \packages\ and update them to the new location. Most of these will be references, but not all of them. For example, WebGrease and Microsoft.Bcl.Build will have separate path settings that will need to be updated. 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 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 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

Thanks for the lengthy answer to my lengthy problem. I will try this solution and get back to you.
Would it work to have both .sln in the same directory? would they end up sharing the same packages directory?
I think this answer demonstrates how rigid nuget is. Having to create such a complex, rigid structure to enable such a simple concept:- "sharing the same assemblies between solutions" is indicative of the poor design of the whole thing.
What also does work to fix the HintPaths - after you updated the NuGet.config to your liking, in Package-Manager console run "Update-Package -reinstall". This will reinstall all packages, fixing their hintpaths too. After this - you might have some relicts left (I had in some silverlight projects - the "old" imports of target/builds still were there)
@Vermis If I setup common nuget packages folder for all my solutions. what will be the impact of that on my Azure Devops build?
A
Adam Wyżgoł

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.


Adam is correct. This is the most simplistic solution to an incredible stupid problem.
It is brilliant solution. simple and clear that doesn't require any code's structure rebuilding.
AFAIK $(SolutionDir) is only set when build is done through VS. So no building directly via msbuild.exe, unless youre setting /p:SolutionDir=path
this could be set automatically here %APPDATA%\Roaming\NuGet\NuGet.Config
This works great until you update the package, and overwrite the HintPath with a new relative path. Becomes pretty tedious in a large solution with a lot of packages. God forbid you have to run an Update-Package -Reinstall...
B
Benjol

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!


This is the recommended way to do it. The old msbuild integration way to do nuget restore in the accepted answer is a pita, and I can confirm putting the NuGet.config alongside the sln works for us with Automatic Package Restore. Putting it in a common parent directory I can't confirm.
How to put NuGet.Config in common parent folder for all solutions (in TFS) so they could refer? Only way I know is .nuget folder under every solution having the file nuget.config; and if "repositorypath value=.packages" then it creates a subfolder under the .nuget like: C:\TFS\Comp\Ent\SolABC\.nuget\.packages - this does not solve the nested projects/solutions in a clean way that should also work on automated TFS build. Accepted answer requires some hand-coded work, plus "repositorypath value=$\..\..\..\Packages this does not work if there are nested projects having different relative paths.
Works with Visual Studio 2015 and Nuget 3.4.3
Not only will it promptly delete the older package, it will overwrite and break the HintPath that you handcoded into the csproj file. @hB0 is correct. This only works for relatively simple solutions. Once you get into a complex TFS workspace structure with branches, shared projects etc. it fails to scale efficiently.
K
Korayem

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.


This is by far the easiest solution of all and it deserves more love!
this is today the easiest solution indeed but one thing is missing in your answer. you still need to deal with HintPath in the csproj files of the each project. They wont be automatically target to new path. am I correct?
Indeed, in some of my old projects there are sometimes references to the "old" packages folder. However for me everything is working correctly, so I guess the global setting takes precedence.
for OSX, see docs.microsoft.com/en-us/nuget/consume-packages/…. I like the mklink idea below, too.
C
Christian

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.


J
Janusz Nowak

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.


O
Oleg Tarasov

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


Unfortunately, looks like Microsoft is moving away from project.json. Also, I like your nuget package. Some time ago I used NuGetReferenceHintPathRewrite which does pretty much the same thing but in different way
a
amarnath chatterjee

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/


u
user803469

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

g
gravidThoughts

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


S
Sudhanshu Mishra

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


Note that there's a missing slash on the closing configuration tag.
s
sgtz

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.


T
Triynko

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.


u
user3285954

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).