Visual Studio 重新构建未修改的项目

86
所以,正如标题所述,我现在有一个包含大约50个项目的VS2010解决方案。如果我对一个“顶层”项目进行更改,而没有任何引用它的内容,那么VS仍然会重新构建所有50个项目。我正在运行没有任何附加组件的Visual Studio 2010 Ultimate。我正在使用ILMerge将所有项目合并为单个文件。
通过检查较低级别的dll时间戳,我已经验证了这一点,并且发现它们确实被重建,即使它们的代码没有被修改。
我已阅读以下所有回复和评论: Visual Studio 2008 keeps rebuilding Visual studio keeps building everything Strange VS2010 build glitch occurs in my solution

在Visual Studio中重新构建C#项目的原因

但是,大多数建议卸载项目以加快构建时间,但没有具体的修复方案。我正在尝试弄清楚为什么VS认为这些依赖项项目需要重新构建,而它们实际上不需要,并进行修复。

我已经打开了“工具>选项>项目和解决方案>生成和运行>仅在运行时构建启动项目和依赖项”,但没有效果。

此外,如果我只重新构建一个“中级”项目,该项目仅有8个(间接)依赖项,则它仍会构建所有8个项目,即使没有调用ILMerge并且没有修改任何依赖项项目。

感谢大家提供的任何见解。

添加

为了测试一些建议,我从头开始创建了一个新的WinForms项目。然后我在该解决方案中创建了两个新项目。我将我两个“最底层”项目的所有代码和资源(不包括项目文件)复制到了这两个全新的项目中(我通过将文件和文件夹从资源管理器中拖放到Visual Studio中的项目中来完成此操作)。

最低的项目,我们称之为B,没有引用任何其他项目。下一个项目A只引用了B。因此,一旦我将所需的.NET和外部程序集引用添加到项目中,解决方案就能够构建。
然后,我让我的新WinForm项目引用A并进行了完整的构建。因此,引用链是: WinForm -> A -> B 然后,我仅修改了WinForm并进行了标准构建(F6)。与以前一样,Visual Studio重新构建了所有三个项目。
经过对项目B源文件的系统性排除,我发现如果我删除了Resources.Designer.csResources.resx(并注释掉使用这些资源的.Properties.Resources对象的代码),那么修改WinForm将不再重建整个解决方案,而只会重建WinForm
Resources.resxResources.Designer.cs添加回项目B(但将引用的代码注释掉,以便没有任何内容使用资源),可以重新引入完整的构建行为。
为了确定我的资源文件是否已损坏,我再次删除它们,然后创建一个新文件(通过项目属性->资源),并重新添加与之前相同的资源,这是一个单独的Excel文件。在此设置下,仍将发生完全重建。
然后我删除了单个资源,但保留了项目B中的资源文件。即使没有添加资源,但仍在项目中保留资源文件,也会发生完全(不必要的)重建。
似乎只要向.NET 3.5项目添加了资源文件,Visual Studio 2010就会始终重建该项目。这是一个错误还是预期的行为?
再次感谢大家!

1
你可以尝试直接使用MSBuild构建它,看看是否仍然会出现这种情况。 - Daniel Powell
1
我的理解是VS调用msbuild(而项目基本上是一个msbuild脚本https://dev59.com/_XI_5IYBdhLWcg3wK_s6),所以我不确定这会改变什么。我怀疑我只会重新创建相同的构建脚本,然后从命令行运行它。直接运行msbuild有什么不同的方法吗?或者有没有办法找出为什么msbuild认为需要重新构建项目? - Matt Klein
2
普通构建(F6)和有时仅项目构建(Shift-F6)。两者表现相同,尽管“仅项目构建”的重建项目范围仅限于当前项目的直接和间接依赖项。 - Matt Klein
@MattKlein 如果你有一个简单的演示项目展示了这个问题,你应该向微软报告 - http://connect.microsoft.com/VisualStudio - Qwertie
我在一个引用了多个手动构建的.csproj文件的解决方案中遇到了这个问题。每个项目都自动包含源代码:<Compile Include="**\*.cs" Exclude="obj\**\*.cs" />。当我删除(不必要的)Exclude属性时,我的依赖检查问题消失了。奇怪的是,我有18个使用这种模式的项目,但只需要更改其中两个就可以让事情正常工作。我怀疑问题在于Visual Studio无法处理所有可能的有效MSBuild语法。 - yoyo
显示剩余4条评论
18个回答

159

打开 工具 - 选项,选择 项目和解决方案 - 构建和运行 ,然后将 "MSBuild 项目构建输出详细程度" 设置为诊断。

原因:构建一个项目时,项目 'ReferencedProject' 不是最新的。项目项 'c:\some.xml' 的“复制到输出目录”属性设置为“始终复制”。

或者

原因:构建一个项目时,输入文件'c:\ReferencedProject.dll'在输出文件'c:\MyProject.pdb'之后被修改。

在这种情况下,需要只在较新时才复制some.xml。

预生成事件和后生成事件也可以触发构建。


32
我认为这是最好的答案,因为它让你真正理解了是什么原因导致构建重复,而不仅仅是猜测或试验。 - Mark Meuer
好的,我知道了!我一直在想为什么一个项目重新构建时也会重建所有引用的项目。诊断日志解释道:“Target "CleanReferencedProjects: (TargetId:8)"” :) - FrankyHollywood
1
谢谢,这正是我所需要的。在我的情况下,问题出在设置为“始终复制”的.ico文件触发了不断的构建。 - Ryan Lundy
2
我也遇到了同样的问题,但是我将日志记录设置为详细模式,它仍然没有输出任何一行告诉我为什么它认为它已经过期。还有其他方法可以找出原因吗? - KFR42
1
你让我开心了。在我的情况下,一些已删除的文件仍然在Solution Explorer中引用(尽管未使用)。重要的是要查找“不是最新的”,因为日志很大。 - user1196549
显示剩余7条评论

18
虽然我认为这不是一个解决方案,但这是一个可行的解决方法,适用于我的情况...最初,我有大约50个项目中的5个包含“资源”部分。这些项目将始终被重建,因此它们所依赖的任何内容也将被重建。其中一个5个项目是一个“基本”级别的库,其他48个项目都引用了它,因此即使不需要它,我的96%项目每次都会被重建。
我的解决方法是使用依赖注入、接口和专门的“资源”项目。我创建了一个接口,用于提供所需的资源,而不是让这5个项目引用自己的“资源”对象。然后,需要这些资源的类在构造函数中创建时需要传入该接口(构造函数注入)。
然后,我创建了一个单独的“资源”项目,其中包含像正常一样的实际资源部分。该项目仅包含资源本身,以及每个需要通过接口提供这些资源的接口的类。该项目将引用具有资源依赖项的每个其他项目,并实现项目所需的接口。
最后,在我的“顶层”项目中,没有任何引用(exe实际上是在此构建的,并且我的组合根位于其中),我引用了“资源”项目,连接了DI,然后我们开始了。
这意味着每次只会重建两个项目(“资源”和“顶层”),如果我进行部分构建(Shift-F6),则它们根本不会被重建。
再次说明,这不是一个很好的解决方法,但是由于每次构建48个项目需要大约3分钟,因此我每天浪费了30到90分钟的时间进行不必要的重建。重构花费了一些时间,但我认为这是一个很好的投资。
这是一个简化的图表。请注意,为了减少混乱,未显示从Main.exeProj1Proj2的依赖项。

Diagram of solution

有了这个设计,我可以构建 Proj1Proj2 而不会触发完整的重建,因为它们没有依赖于 Resources 部分。只有 Main 知道关于 Resources 实现的内容。

17

当项目中存在一个实际不存在的文件时,就会出现这种情况。
由于该文件不存在,因此项目无法确定该文件是否已更改,因此需要重新构建。

只需查看项目中的所有文件,并搜索那个没有可展开箭头的文件。


这也是我的问题,但在我的情况下,由于文件最近已被删除,所以旁边有一个可展开的箭头。因此,您可能需要更仔细地查找导致重新链接的已删除文件。 - Malvineous
2
如果我有七千万个文件怎么办? - Sandra K
1
你可以创建一个小脚本/程序,遍历项目文件或.filter文件,并检查引用的文件是否存在。 - Yochai Timmer

11

我在使用VS 2015时也遇到了同样的问题。

对我有用的方法是:

  1. 一个项目在某个其他项目bin文件夹中引用了自身的副本(神奇,是的)。可以通过切换到诊断构建输出(在构建选项中)并从项目层次结构的顶部逐个构建项目来找到这种情况——如果你看到重新构建了项目,即使没有任何更改,那么就要查看它的引用。
  2. 我把所有项目中的"copy always"文件都改成了"copy if newer"。基本上,在所有.csproj文件中将<CopyToOutputDirectory>Always</CopyToOutputDirectory>替换为<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  3. 然后像这篇文章中所述,使用这个powershell脚本禁用NTFS隧道:

New-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "MaximumTunnelEntries" -Value 0 -PropertyType "DWord"

之后我需要重新构建一下,现在似乎可以工作了。


有没有一个快速的方法可以做到这一点(例如,我可以搜索某些特定的文本)?我在VS 2015中遇到了同样的问题,我的解决方案文件相当大(包括一些我不允许修改的项目,因为它们不属于我)。 - EJoshuaS - Stand with Ukraine

5
在我的情况下,罪魁祸首是一个被引用的dll的“Copy Local”设置为true,以及一个文件的“Copy to Output Directory”设置为“始终复制”。

4

针对 .Net Core 项目,以上所有解决方案均不起作用。

我已经找到了解决方案。如果你使用的是 Visual Studio 2019:

  1. 构建解决方案两次
  2. 打开 工具 -> 选项 -> 项目和解决方案 -> SDK 风格的项目 -> 日志级别 -> 详细
  3. 清空输出窗口
  4. 构建你的起始项目
  5. 检查输出窗口。 所有以 FastUpToDate 开头的字符串
  6. 你会发现一些项目项使你的项目不是最新的。
  7. 修复这些问题并从步骤1重新开始。 如果你的修复方法正确,你将在构建输出的最后一个字符串中获得 Build: 0 succeeded, 0 failed, {n} up-to-date, 0 skipped

简单的脚本现在可以打印缺失的文件(C++项目)https://dev59.com/3GAf5IYBdhLWcg3w_W9j#69647948 - Tom

3

1
非常有用的链接。在那里看到你的工具也被提到了,真的很棒!我可能会试着使用它来调试我们的一个解决方案。 - julealgon

2
另一个经常出现的问题是,解决方案中某个项目的修改时间戳在未来。如果你将时钟向前调整,然后再将时钟设置回正确的时间,就会发生这种情况。我在安装Linux时遇到了这个问题。
在这种情况下,你可以使用git bash(是的,在Windows中)递归地触碰所有文件:
find . -exec touch {} \;

2

我曾经遇到过同样的问题,原来是由于一些项目中有一个本地引用dll文件的副本在它们自己的输出目录中。

找到这个问题的关键是将诊断输出设置为构建输出,并知道在日志中要查找什么。搜索:“not up to date”是关键。


1
根据您的观察,听起来您的项目表达了对其他项目的依赖,但这种依赖并不明显。在项目文件中,可能会存在孤立的依赖项而不在用户界面中显示。您是否在文本编辑器中打开一个出现问题的项目文件并查看其解决方案构建依赖项?如果您找不到任何问题,请尝试从头开始重新创建一个项目,以查看新项目是否存在相同的问题。如果干净的项目可以正确构建,则说明您在某个地方表达了不需要的依赖关系。据我所知,除非您有Makefile或其他异常的构建步骤,否则这些依赖关系必须在项目文件或解决方案文件中表达。

经过一番深入的研究,看起来 Resources.resx 可能是这种行为的原因。我在我的问题中添加了信息来描述我的实验。 - Matt Klein
从您提供的新信息中并不清楚您将.resx文件添加到哪个项目中。无论如何,在测试时要小心,手动删除所有构建创建的文件夹,以确保早期剩余的文件不会影响后来的测试。 - Owen Wengerd
结果很奇怪,但不幸的是我没有任何新的补充。除此之外,我会继续将问题缩小到最低限度(只有两个项目而不是三个,只有新的未修改向导创建的项目,并进行最少量的必要更改以重现问题等),然后看看是否有其他人可以使用相同的项目文件重现问题。如果可以,请通过Microsoft Connect提交错误报告。 - Owen Wengerd
抱歉我没有说明我正在调整哪个项目。我已经更新了陈述以表明我正在修改最低(B)项目的资源文件。我会继续探索这个问题,因为我可能每天要浪费1个小时在不必要的重建上。 - Matt Klein

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接