【问题标题】:How does Visual Studio know my project is up to date so it can skip running MSBuild?Visual Studio 如何知道我的项目是最新的,因此它可以跳过运行 MSBuild?
【发布时间】:2016-01-30 21:25:51
【问题描述】:

我的 C++ 项目中包含一个自定义 MSBuild 目标,该目标在 $(OutDir) 文件夹中为给定项目类型的每个项目生成一个数据文件。我将项目类型与属性页架构挂钩,因此您可以在解决方案资源管理器中的文件上选择它,并且我的目标声明输入和输出,以便增量构建工作。我还将我的目标添加到 $(BuildDependsOn) 属性中,以便在构建目标 Visual Studio 调用期间自动评估它。

似乎一切正常,除了一件事:如果我删除了 $(OutDir) 中的一个输出数据文件,然后构建 Visual Studio 什么都不做,并说我的项目是最新的。如果我删除项目生成的 exe 文件或触摸其中一个 MSBuild 脚本的修改时间,Visual Studio 会重新评估目标并发现输出文件丢失,导致使用我的目标重新构建它。

从 MSBuild 诊断日志来看,Visual Studio 似乎在内部维护一些输出文件和输入文件的列表,它会检查这些文件以避免评估 MSBuild 脚本。如何将我的输出文件添加到此列表中?

【问题讨论】:

  • 一般来说,你不能做太多。您可以将此报告给 Microsoft Connect,看看其他人是否有解决方法。
  • 我投票决定将此问题作为离题结束,因为它与供应商产品相关,而没有太多关于解决此问题的技术细节的公开文档。
  • @LexLi 如果我们经常在没有太多文档的情况下关闭与供应商产品相关的问题,那么将会很多不那么有趣

标签: visual-studio msbuild visual-studio-2015


【解决方案1】:

MsBuild/VS 确实有一种机制来确定输入文件是最新的,它围绕一个可执行的 tracker.exe 展开,它扫描 .tlog 文件以确定项目的输出文件是什么。可能还有更多内容,如果您在互联网上四处查看,您可能会获得更多相关信息。

但问题是您实际上并不需要了解它的每一个细节:您可以在检查内置 CustomBuildStep 的工作原理时找到一个简单的使用示例并将其应用于你的情况。我将简要解释一下我是如何做到这一点的,因为我认为它在处理此类 msbuild 问题时也可能对您有用。

如果你添加

<ItemDefinitionGroup>
  <CustomBuildStep>
    <Command>echo foo &gt; $(OutDir)\foo.txt</Command>
    <Outputs>$(OutDir)\foo.txt</Outputs>
  </CustomBuildStep>
</ItemDefinitionGroup>

手动或通过自定义构建步骤的项目属性页,您将看到行为正是您所需要的:如果删除 foo.txt,构建将开始,而构建被标记如果不是最新的(好吧,并且当其余的输出也是最新的)。

因此,关键是做 CustomBuildStep 在后台所做的事情,并弄清楚这一点只需使用您选择的工具在 下的所有文件中搜索所有出现的 CustomBuildStep C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120(根据使用的平台/VS 版本调整路径)。

这将我们带到 Microsoft.CppCommon.Targets,其中名为 CustomBuildStep 的目标(请注意,这与上面的 ItemDefinitionGroup 中的条目名称相同)调用实际的 CustomBuildStep 命令。它还有一个特别有趣的地方:

<!-- Appended tlog to track custom build events -->
<WriteLinesToFile Encoding="Unicode"
  File="$(TLogLocation)$(ProjectName).write.1u.tlog"
  Lines="@(CustomBuildStep->'^%(Identity)');@(CustomBuildStep->MetaData('Outputs')->FullPath()->Distinct())"/>

因此,这会将输出的路径写入跟踪器使用的目录中的 .tlog 文件,并使其按需要工作。有关格式的更多信息,另请参阅here

tl;dr 使用 WriteLinesToFile 将目标输出的完整路径附加到 $(TLogLocation)$(ProjectName).write.1u.tlog 之类的文件中。我说 like 因为 write.tlog、write.u.tlog 等也可以。

【讨论】:

  • 这是一篇精彩的帖子,我会投赞成票,但我仍然觉得“你不需要了解它的每一个细节”这部分很烦人。我留下了我之前的评论,因为虽然 MSBuild 现在是完全开源的,但 VS 不是,它确实以不同的方式构建项目。很难获得洞察力并通过挖掘进行适当的修复。如果不完全了解细节,仅仅修复并不能保证它长期有效。我们很幸运拥有像您这样的专家,但在大多数情况下,我更愿意求助于可以访问代码库的 Microsoft 支持。
  • @LexLi 可惜没有更多的文档可用于此,您确实提出了一个令人信服的观点,但它有时会进入宗教领域,即“人们需要了解多少工具才能工作用它'。例如,我对浏览器的内部一无所知,我可以很好地使用它。当然它是开源的,如果出现问题可能有助于我更好地找到和理解 ifx,但只是修复的细节完全清楚这一事实并不提供任何保证它会以某种方式工作比用较少的细节进行修复。
  • 这行得通,谢谢。尽管msdn.microsoft.com/en-us/library/ms366724.aspx 微软没有在这里记录这一点是没有意义的
【解决方案2】:

Visual Studio 使用称为 Visual Studio 通用项目系统 (CPS) (https://github.com/Microsoft/VSProjectSystem) (VS 2017) 的东西 管理项目,包括构建过程。

在 CPS 中,任何实现 IBuildUpToDateCheckProvider 接口的东西都可以使用 作为项目的“UpToDateChecker”。 在调用 MsBuild 之前调用“UpToDateChecker”。其主要目的是判断是否调用 MsBuild 来构建项目,或者将项目标记为“Up To Date”并一直跳过 msbuild。

这个“UpToDateChecker”正是打印到诊断构建输出中的内容:

1>------ 最新检查:项目:“ProjectName”,配置: 调试 x86 ------ 项目不是最新的:构建输入“header.h”是 在构建输出“a.out”后修改。输入时间:12/27/2018 4:43:08 下午,输出时间:1/1/0001 2:00:00 AM

对于 C++ 项目,对于 VS 2017,其默认的“UpToDateChecker”是 VCProjectBuildUpToDateCheck (Microsoft.VisualStudio.Project.VisualC.VCProjectEngine.dll)。 首先,它会在 tlogs 目录(通常类似于 Debug\x86\.tlog)中查找这些文件:

  • .lastbuildstate
  • 构建失败
  • 所有 '.read..tlog' - 输入文件,在诊断构建输出中标记为“构建输入”
  • 所有 '.write..tlog' - 输出文件,在诊断构建输出中标记为“构建输出”

其实还有更多的检查,但是大多数失败都是在检查这4种时发生的

【讨论】:

  • 你知道吗,我在哪里可以找到 C# 项目的默认 UpToDateChecker
  • 我最好的猜测是 Microsoft.VisualStudio.ProjectSystem.Managed.VS.dll。有 BuildUpToDateCheck.IsUpToDateAsync()
  • 我已经找到了。它位于csproj.dll。但由于这是一个本机 dll,因此很难阅读。 Microsoft.VisualStudio.ProjectSystem.Managed.dll 包含 .net 核心项目和共享项目的新实现。
  • @roli09 csproj.dll 是 C# 项目的旧项目系统。它不是基于 CPS,是封闭源代码,不支持 SDK 样式项目等更新功能。正如 Mariia N 所说,实现 CPS 的IBuildUpToDateCheckProvider 的更现代的项目系统是Microsoft.VisualStudio.ProjectSystem.Managed.VS.dll。它也是开源的。你可以在github.com/dotnet/project-system找到实现。
【解决方案3】:

您可以通过在注册表项中启用快速更新检查器的详细程度来了解为什么要重建项目:

New-ItemProperty              `
 -Name U2DCheckVerbosity      `
 -PropertyType DWORD -Value 1 `
 -Path HKCU:\Software\Microsoft\VisualStudio\14.0\General -Force

您应该能够在构建日志中看到类似

的消息

“Caliburn.Micro.Silverlight.Extensions”项目不是最新的。项目项“C:\dev\projects\Caliburn.Micro.Silverlight.Extensions\NavigationBootstrapperSample.cs.pp”的“复制到输出目录”属性设置为“始终复制”。

[1]https://blogs.msdn.microsoft.com/kirillosenkov/2014/08/04/how-to-investigate-rebuilding-in-visual-studio-when-nothing-has-changed/

【讨论】:

    【解决方案4】:

    为旧式项目(即非 SDK 式项目,在 .NET Framework 时代常见)启用日志记录:

    1. 为您正在使用的特定版本的 Visual Studio 打开“开发人员命令提示符”。
    2. 输入命令:
      vsregedit set "%cd%" HKCU General U2DCheckVerbosity dword 1
      
    3. 应该显示消息Set value for U2DCheckVerbosity

    使用0 而不是1 运行相同的命令以禁用此日志记录。

    更多信息请访问:https://github.com/dotnet/project-system/blob/main/docs/up-to-date-check.md#net-framework-projects

    【讨论】:

      【解决方案5】:

      此处的原始问题与 C++ 项目有关,但对于在搜索有关现代(SDK 样式)C#/VB/F# 项目的信息时发现此问题的任何人,您可以自定义 Visual Studio 的快速最新检查,如中所述本文档:

      https://github.com/dotnet/project-system/blob/master/docs/up-to-date-check.md

      简而言之,您将输入和输出指定为项目:

      • UpToDateCheckInput — 描述一个 MSBuild 不会知道的输入文件
      • UpToDateCheckBuilt — 描述一个 MSBuild 不会知道的输出文件

      通过此设置提高诊断日志记录级别以进行最新检查非常有帮助:

      【讨论】:

        猜你喜欢
        • 2013-09-14
        • 2019-05-30
        • 2011-07-12
        • 1970-01-01
        • 1970-01-01
        • 2011-08-28
        • 1970-01-01
        • 2022-01-10
        • 2011-02-13
        相关资源
        最近更新 更多