当多个解决方案中包含某些项目时,为所有解决方案设置一个公共的nuget软件包文件夹


137

我一直在使用NuGet从外部和内部包源中检索包,这非常方便。但是我已经意识到,默认情况下,每个解决方案都存储了这些软件包,当某些解决方案中包含一些带有NuGet引用的项目时,这非常令人沮丧。然后,将引用更改为其他解决方案包文件夹,而该文件夹实际上对于其他开发人员或构建机器不可用。

我已经看到有一些方法可以指出NuGet的2.1发行版中的通用软件包位置(也许是在项目根级别,我们正在使用TFS源代码控制),请参见发行说明。我正在使用NuGet v2.7

但是我尝试添加nuget.config文件,但没有看到任何效果。程序包仍存储在解决方案文件夹中。有什么我想念的吗?似乎要添加到nuget.config文件中的xml节点结构不同,具体取决于谁在回答这个问题:Schwarzie在另一个Stackoverflow线程上建议:

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

NuGet 2.1的发行说明(请参见上面的链接)建议采用以下格式:

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

我不知道其中哪一个或最后一个或两者都可以工作。我在解决方案级别都尝试过。nuget.config文件可以放在TFS项目根目录下,还是必须在解决方案目录中?似乎NuGet按一定顺序读取并应用了这些文件中的设置,为什么将它们添加到多个级别是有意义的,因此解决方案级别的nuget.config文件将覆盖TFS项目根级别的一个。可以澄清一下吗?

在这些引用生效之前,我需要删除所有已安装的软件包吗?如果有人可以提供从解决方案特定的nuget用法转移到公用包文件夹的分步说明,在该文件夹中属于多个解决方案的项目可以找到所需的nuget包,我将非常高兴。


3
我怀疑您的问题的简短答案(隐藏在Vermis的下面的答案中)是您$在相对路径前面缺少。另外,关于NuGet.Config文件的问题的答案在这里。它首先在.nu​​get,然后在所有的父目录,然后在你的应用程序数据的“全球性”文件:时使它们以相反的顺序(无论手段)。
Benjol 2013年

这似乎很难。有一个名为Paket的工具可以解决这个问题:fsprojects.github.io/Paket
Tuomas Hietanen 2015年

后期评论。只是想添加一下,如果在开始此更改时运行Visual Studio,则需要重新启动它,因为nuget.config文件似乎在VS启动期间读取。另外,没有$就没有问题,只是不要以反斜杠开头。
MBWise

Answers:


147

对于外部和内部包源,在多个解决方案中引用的项目,我也有类似的情况。今天,我刚刚使用我们的一个代码库进行了这项工作,并且似乎正在使用开发人员工作站和构建服务器。下面的过程考虑了这种情况(尽管不难适应将通用包文件夹放在其他位置)。

  • 程式库
    • 项目A
    • 项目B
    • 项目C
    • 解决方案
      • 解决方案1
      • 解决方案2
      • 解决方案3
      • 包(这是所有解决方案共享的通用包)

从Visual Studio 2015 Update 3的NuGet 3.5.0.1484开始更新的答案

现在,此过程比我最初解决此问题并认为是时候进行更新时要容易一些。通常,该过程是相同的,只是步骤更少。结果是解决或提供以下内容的过程:

  • 解决方案中可见并跟踪需要提交给源代码控制的所有内容
  • 在Visual Studio中使用“程序包管理器”安装新程序包或更新程序包将使用正确的存储库路径
  • 初始配置后,不会对.csproj文件进行黑客攻击
  • 无需修改开发人员工作站(签出时即可构建代码)

有一些潜在的缺点要注意(YMMV,我还没有经历过)。请参阅下面的Benol答案和评论。

添加NuGet.Config

您将要在\ Solutions \文件夹的根目录中创建一个NuGet.Config文件。确保这是您创建的UTF-8编码文件,如果不确定如何执行此操作,请使用Visual Studio的“文件”->“新建”->“文件”菜单,然后选择“ XML文件”模板。将以下内容添加到NuGet.Config中:

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

对于repositoryPath设置,可以使用$令牌指定绝对路径或相对路径(推荐)。$令牌基于NuGet.Config的位置($令牌实际上是相对于NuGet.Config位置下方的一级)。因此,如果我有\ Solutions \ NuGet.Config并想要\ Solutions \ Packages,则需要指定$ \ .. \ Packages作为值。

接下来,您需要向解决方案中添加一个名为“ NuGet”的解决方案文件夹(在解决方案上单击鼠标右键,依次单击“添加”->“新解决方案文件夹”)。解决方案文件夹是仅存在于Visual Studio解决方案中的虚拟文件夹,不会在驱动器上创建实际的文件夹(您可以从任何地方引用文件)。右键单击“ NuGet”解决方案文件夹,然后单击“添加”->“现有项”,然后选择\ Solutions \ NuGet.Config。

我们这样做的原因是,使其在解决方案中可见,并应有助于确保将其正确提交给您的源代码控件。您可能要对参与共享项目的代码库中的每个解决方案执行此步骤。

通过将NuGet.Config文件放置在任何.sln文件上方的\ Solutions \中,我们利用了NuGet将从“当前工作目录”向上递归导航文件夹结构以查找要使用的NuGet.Config文件这一事实。这里的“当前工作目录”意味着几件不同的事情,一个是NuGet.exe的执行路径,另一个是.sln文件的位置。

切换包裹文件夹

首先,我强烈建议您浏览每个解决方案文件夹,并删除任何存在的\ Packages \文件夹(您需要先关闭Visual Studio)。这样可以更轻松地查看NuGet将新配置的\ Packages \文件夹放在何处,并确保指向错误的\ Packages \文件夹的任何链接都会失败,然后可以修复。

在Visual Studio中打开您的解决方案,然后开始“全部重建”。忽略您将收到的所有构建错误,这是目前所希望的。但是,这应该在构建过程开始时启动NuGet软件包还原功能。验证是否已在所需位置创建\ Solutions \ Packages \文件夹。如果还没有,请检查您的配置。

现在,对于解决方案中的每个项目,您将需要:

  1. 右键单击项目,然后选择“卸载项目”
  2. 右键单击项目,然后选择编辑your-xxx.csproj
  3. 查找对\ packages \的任何引用,并将其更新到新位置。
    • 其中大多数将是<HintPath>引用,但不是全部。例如,WebGrease和Microsoft.Bcl.Build将具有需要更新的单独的路径设置。
  4. 保存.csproj,然后在项目上单击鼠标右键,然后选择“重新加载项目”

一旦所有.csproj文件都已更新,请启动另一个“全部重建”,您就不会再有关于缺少引用的构建错误。至此,您已完成工作,现在将NuGet配置为使用共享的Packages文件夹。

从VStudio 2012开始的NuGet 2.7.1(2.7.40906.75)

首先要记住的是nuget.config不能控制nuget软件包系统中的所有路径设置。弄清楚这一点特别令人困惑。具体来说,问题在于msbuild和Visual Studio(称为msbuild)不在nuget.config中使用路径,而是在nuget.targets文件中覆盖了该路径。

环境准备

首先,我将浏览解决方案的文件夹,并删除所有存在的\ packages \文件夹。这将有助于确保将所有软件包可见地安装到正确的文件夹中,并有助于发现整个解决方案中的任何错误路径引用。接下来,我将确保您安装了最新的nuget Visual Studio扩展。我还要确保您在每个解决方案中都安装了最新的nuget.exe。打开命令提示符,进入每个$(SolutionDir)\ .nuget \文件夹,然后执行以下命令:

nuget update -self

设置NuGet的通用软件包文件夹路径

打开每个$(SolutionDir)\ .nuget \ NuGet.Config,然后在<configuration>部分中添加以下内容:

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

注意:您可以使用绝对路径或相对路径。请记住,如果您使用带有$的相对路径,则该路径相对于NuGet.Config位置下方一个级别(请相信这是一个错误)。

设置MSBuild和Visual Studio的通用软件包文件夹路径

打开每个$(SolutionDir)\ .nuget \ NuGet.targets并修改以下部分(请注意,对于非Windows,下面还有另一部分):

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

将PackagesDir更新为

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

注意: GetFullPath会将我们的相对路径解析为绝对路径。

将所有nuget软件包还原到公共文件夹

打开命令提示符并转到每个$(SolutionDir)\ .nuget并执行以下命令:

nuget restore ..\YourSolution.sln

此时,您应该在公共位置只有一个\ packages \文件夹,而在任何解决方案文件夹中都没有。如果没有,请验证您的路径。

修复项目参考

在文本编辑器中打开每个.csproj文件,找到对\ packages的所有引用,并将它们更新为正确的路径。其中大多数将是<HintPath>引用,但不是全部。例如,WebGrease和Microsoft.Bcl.Build将具有需要更新的单独的路径设置。

建立您的解决方案

在Visual Studio中打开解决方案,然后开始构建。如果它抱怨丢失的软件包需要还原,请不要以为该软件包丢失并且需要还原(错误可能会引起误解)。您的.csproj文件之一中的路径可能不正确。还原软件包之前,请先进行检查。

是否有关于缺少软件包的构建错误?

如果您已经验证了.csproj文件中的路径正确,则可以尝试两种方法。如果这是从源代码控制中更新代码的结果,则可以尝试签出干净副本,然后进行构建。这对我们的一名开发人员有效,我认为.suo文件中或类似的东西。另一个选择是使用相关解决方案的.nuget文件夹中的命令行手动强制软件包还原:

nuget restore ..\YourSolution.sln

感谢您对我冗长的问题的冗长回答。我将尝试此解决方案,并尽快与您联系。
Mats Isaksson

将两个.sln放在同一个目录下是否可行?他们最终会共享相同的软件包目录吗?
tofutim

7
我认为这个答案证明了硬核是多么坚硬。必须创建如此复杂,僵化的结构才能实现这样一个简单的概念:-“在解决方案之间共享相同的组件”表明整个设计的糟糕。
米克,2016年

1
修复HintPath的方法也起作用-在根据您的喜好更新了NuGet.config之后,在Package-Manager控制台中运行“ Update-Package -reinstall”。这将重新安装所有软件包,并修复其提示路径。之后-您可能还有一些遗物(我在一些Silverlight项目中-目标/构建的“旧”进口仍然存在)
johannes.colmsee '18

1
@Vermis如果我为所有解决方案设置通用的nuget软件包文件夹。这对我的Azure Devops版本有什么影响?
Pankaj Devrani

39

除了为所有项目设置通用包位置之外,还可以如下更改项目中的HintPath:

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

在大多数情况下,共享项目中只有很少的软件包,因此您可以轻松进行更改。

我认为这是一个更好的解决方案,当您在分支代码时,设置通用存储库时,必须更改相对路径,在此解决方案中,您无需这样做。


2
亚当是正确的。对于一个令人难以置信的愚蠢问题,这是最简单的解决方案。
lapsus

1
这是绝佳的解决方案。简单明了,不需要任何代码的结构重建。
RredCat 2015年

2
仅当通过VS完成构建时,才设置AFAIK $(SolutionDir)。因此,除非您设置/ p:SolutionDir = path
Lars Nielsen

这可以在此处自动设置%APPDATA%\ Roaming \ NuGet \ NuGet.Config
Korayem '16

15
在您更新程序包并用新的相对路径覆盖HintPath之前,这很有用。在包含许多软件包的大型解决方案中变得非常乏味。上帝禁止您必须运行更新程序包
重新

22

我在使用最新版本的NuGet(2.7)和VS2012进行尝试时的经验:

  • 删除.nuget文件夹(在磁盘上和解决方案中)
  • 将NuGet.Config文件放在所有解决方案的公共父文件夹中
  • 删除任何现有的软件包文件夹
  • 遍历所有csproj文件并更改HintPaths以指向新位置
  • 利润

就我而言,我想将所有软件包都放在中.packages,因此我的NuGet.Config如下所示。

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

请注意,可能会发生一些“奇怪”的事情,但是我认为它们是可以忍受的:

  • 如果您从一个解决方案“更新”一个软件包,它将立即从您的packages文件夹中删除较旧的版本(它不知道您是否有另一个解决方案指向该文件夹)。这并不会给我带来太大的麻烦,因为其他解决方案只会在需要时恢复。
  • 如果您尝试通过右键单击解决方案来添加软件包,如果该软件包已经存在于另一个解决方案中,它将看到该软件包已存在,并显示“绿色勾号”而不是“安装”按钮。我通常从项目上单击鼠标右键进行安装,因此这根本不会困扰我。

免责声明:我今天才尝试此操作,我没有任何长期经验来支持它!


4
这是做推荐的方式。在接受的答案中执行nuget恢复的旧msbuild集成方法是皮塔饼,并且我可以通过自动软件包恢复来确认将NuGet.config与sln一起使用对我们而言是可行的。我无法确认将其放置在公共父目录中。
9swampy

1
如何将NuGet.Config放在所有解决方案的公共父文件夹中(在TFS中),以便它们可以引用?我知道的唯一方法是每个解决方案下的.nuget文件夹都有nuget.config文件;如果“ repositorypath value = .packages”,那么它将在.nuget下创建一个子文件夹,如:C:\ TFS \ Comp \ Ent \ SolABC \ .nuget \ .packages-这不能以干净的方式解决嵌套项目/解决方案这也应该适用于自动TFS构建。接受的答案需要一些手工编码的工作,再加上“repositorypath值= $ \ .. \ .. \ .. \包,这并不工作,如果有不同的相对路径嵌套的项目。
HB0

2
与Visual Studio 2015的NuGet 3.4.3工程
侯赛因Yağlı

它不仅会立即删除较旧的程序包,还将覆盖并破坏您手动编码到csproj文件中的HintPath。@ hB0是正确的。这仅适用于相对简单的解决方案。一旦进入具有分支,共享项目等的复杂TFS工作空间结构,它就无法有效扩展。
gravidThoughts

20

我有VS 2013的NuGet版本2.8.50926。您不需要使用多个nuget.config文件,也不需要使用复杂的目录结构。只需修改位于以下位置的默认文件:

%APPDATA%\Roaming\NuGet\NuGet.Config

这是我文件的内容:

<?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>

因此,无论解决方案在哪里,所有软件包都转到“ C:\ Projects \ nugetpackages”文件夹。

在所有解决方案中,只需删除现有的“ packages”文件夹。然后构建解决方案,NuGet会自动将丢失的软件包还原到您指定的新的集中式目录中。


到目前为止,这是所有方法中最简单的解决方案,值得更多的关注!
Korayem '16

2
这确实是今天最简单的解决方案,但是您的答案中缺少一件事。您仍然需要在每个项目的csproj文件中处理HintPath。它们不会自动定位到新路径。我对么?
batmaci

确实,在我的一些旧项目中,有时会引用“旧”包文件夹。但是对我来说,一切工作正常,因此我认为全局设置优先。
Matthieu

对于OSX,请参阅docs.microsoft.com/en-us/nuget/consume-packages/…。我也喜欢下面的mklink想法。
sgtz


3

从Visual Studio 2013 Update 4和Nugget软件包管理器版本> 2.8.5 ...

在存储库的根目录中创建nuget.config文件。

文件内容:

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

这将导致所有软件包都将进入nuget.config文件级别的packages文件夹。

现在,您可以使用命令'update-package -reinstall'进入每个.sln nuget控制台。

如果您在同一级别拥有多个存储库,并且希望在它们之间共享相同的软件包文件夹,请尝试使用上一个文件夹的方法。

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

但是,这种方式会使nuget包引用csproj指向存储库路径之外的一个文件夹。


3

我设计了一个NuGet包,该包将项目中的所有NuGet引用透明地转换为$(SolutionDir)相对格式。它使用构建时XSLT转换来做到这一点,因此您无需手动修改项目文件。您可以自由更新软件包,它不会破坏任何内容。

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

或者,如果您使用Visual Studio 2015 Update 3,则可以按照以下说明将程序包引用迁移到project.json窗体:https : //oren.codes/2016/02/08/project-json-all-the-things


不幸的是,看起来微软正在远离p​​roject.json。另外,我喜欢你的nuget包。前段时间,我使用了NuGetReferenceHintPathRewrite,它执行的操作几乎相同,但是方式不同
Andrey Borisko 2016年

2

更新我对nuget 2.8.3的体验。这是相对简单的。所有操作均启用了从右键单击解决方案还原程序包的功能编辑了NuGet.Config并添加了以下行:

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

然后重建解决方案,将所有软件包下载到我所需的文件夹中,并自动更新参考。对于其他所有项目,我都进行了相同的操作,在这些项目中,仅下载了增量软件包,并引用了现有软件包。因此,已为所有项目设置了通用的软件包存储库。

这是启用程序包还原的分步过程。

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


2

只需将/ packages硬链接到所需的共享位置。这样,您的项目就不会被没有特殊软件包位置的其他用户破坏。

以管理员身份打开命令提示符并使用

mklink /prefix link_path Target_file/folder_path

2

对于任何重要的解决方案,上述答案都将不足。很简单,复杂的TFS工作空间结构具有不同的相对路径,分支,共享项目等,因此不可能有一个中央存储库。

使用($ SolutionDir)是朝正确方向迈出的一步,但是用($ SolutionDir)手工编码csproj文件在具有数百个定期更新的包的代码库中将变得非常乏味(每次更新发生时,HintPath都会被覆盖)新的相对路径)。如果必须运行Update-Package -Reinstall,会发生什么情况。

有一个很棒的解决方案,称为NugetReferenceHintPathRewrite。它会在构建之前自动将($ SolutionDir)注入到HintPaths中(无需实际更改csproj文件)。我想它可以轻松地合并到自动化构建系统中


1

VS 2013专业版(带有NuGet版本)的简短摘要:2.8.60318.667

这是将软件包定向到相对于.nuget文件夹的路径的方式:

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

例如,如果您的解决方案(.sln文件)位于C:\ Projects \ MySolution中,则在启用NuGet软件包还原时,将按如下方式创建.nuget文件夹:C:\ Projects \ MySolution.nuget并将下载软件包到这样的目录:C:\ Projects \ MySolution \ Dependencies

注意:由于某些(未知)原因,每次我更新“ repositoryPath”时,都必须关闭并重新打开解决方案以使更改生效


请注意,结束配置标记上缺少斜杠。
Moo 2015年


0

对于使用paket作为其包管理器的用户,您的依赖项文件有一个自动符号链接选项:

storage: symlink

顺便说一句:包利用nuget

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

如果您希望尽可能少地进行修改,则可以使用脚本定期清理子目录。即。Nuget的默认行为是将文件从全局位置复制到本地程序包文件夹,因此请随后删除这些文件。


0

我只使用NTFS联结将所有包文件夹直接定向到存储库根上方的单个文件夹。效果很好。跨多个解决方案的并行程序包还原没有问题。这样做的一个好处是,您根本不需要重新配置源代码中的任何内容,例如.csproj文件中的数百个相对提示路径。通过让文件系统处理单个包文件夹的重定向和模拟,它“起作用”。

只要注意“ git”问题。尽管通过命令行的“ git status”没有显示未暂存的更改,但我注意到GitKraken将“ package”联结视为未暂存的文件。单击它甚至会显示诸如“文件是目录”之类的错误。如果您重新设置基准,销毁连接并将其还原为包含原始文件路径文本的实际文件,则GitKraken还将尝试隐藏此“文件”。非常奇怪的行为。也许可以通过确保将包文件添加到您的.gitignore中来解决此问题。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.