Visual Studio如何确定是否需要启动MSBuild?

3
我过去为我的C++项目进行了很多MSBuild定制。MSBuild目标的InputOutput属性用于确定是否需要执行该目标。此外,Visual Studio使用位于中间目录中的.tlog文件来确定是否需要调用MSBuild。
现在我正在处理一个C#项目。我编写了一个简单的MSBuild目标,将文件复制到输出目录:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="CopyMyFile" BeforeTargets="AfterBuild" Inputs="$(ProjectDir)File.dat" Outputs="$(TargetDir)FileRenamed.dat">
    <Copy SourceFiles="$(ProjectDir)File.dat" DestinationFiles="$(TargetDir)FileRenamed.dat" OverwriteReadOnlyFiles="true">
    </Copy>
  </Target>
</Project>

如果通过MSBuild.exe调用构建,则目标按预期工作。如果目标文件不存在或源文件已被修改,则会复制该文件。
如果我在Visual Studio中调用构建,则不会按预期工作。如果我从输出目录中删除文件,则Visual Studio不会调用MSBuild。另一方面,每次我修改源文件并构建项目时,都会调用MSBuild,即使没有进行其他更改。
似乎Visual Studio只比较项目中的每个文件与输出文件(.exe、.dll或.pdb)。如果项目中的任何文件比输出文件更新,则会调用MSBuild。在我的情况下,MSBuild不会更新.exe文件,因此会一遍又一遍地调用MSBuild。
在C++项目中,这种行为由.tlog文件控制。在C#项目中有类似的东西吗?
非常感谢!

.tlog文件并不是直接由MSBuild使用的,而是由Visual Studio用来决定是否需要调用MSBuild。我过去常常为我的自定义构建目标创建自己的.tlog文件,以控制何时激活Visual Studio调用MSBuild,何时不调用。我希望对于C#项目也有类似的技术可用。 - alerosmile
你确定吗?即使不使用VS构建C++项目,只要使用msbuild,就会创建tlog文件(并且很可能在下一次构建时再次读取)。 - stijn
你是对的。抱歉之前评论有误。但是,Visual Studio使用.tlog文件来决定是否需要调用MSBuild的假设仍然是正确的。但无论如何,这并不重要。我需要的是一种告诉Visual Studio何时调用MSBuild以及何时不调用的方法,因为Visual Studio对我的自定义构建目标一无所知。 - alerosmile
我现在没有时间给出答案,但我相当确定VS仅使用时间戳来比较文件。因此,您必须以某种方式指示它“看到”您的文件。要弄清楚如何做到这一点,您需要查看例如Content是如何实现的:如果您只是将文本文件添加到项目中,则会将其添加到Content ItemGroup中,并且如果将其CopyToOutputDirectory元数据设置为PreserveNewest,则会自动获得您想要的行为,在VS中,因此可能有一种方法可以模拟此自定义目标。 - stijn
2个回答

0

答案可能是没有,没有类似于tlog机制的东西。虽然我不是100%确定,但这很奇怪,因为你甚至不能做一些基本的事情,这意味着微软基本上放弃了C#(和类似项目)的跟踪器功能,但没有用用户可以连接到的东西来替换它。

使用procmon,您可以看到VS获取输出和输入文件的时间戳,但我找不到任何干涉其处理为输入和输出文件的方法。看起来VS获取直接包含在项目文件中的所有内容的列表(即在VS中显示的Reference/Content/Compile/..项组),而不是列在Taget's Inputs/Outputs中的内容,并在构建开始时比较这些项的时间戳。如果一切都是最新的(也就是说,就VS而言),则不会启动msbuild进程进行构建。

有一个解决方法,虽然不是很好:如果您添加一个“虚拟”的内容项(例如右键单击项目->添加新项->文本文件)并将其设置为始终被复制(右键单击刚添加的文本文件->属性->复制到输出目录->始终复制),那么VS将始终启动构建,因此检查您的目标输入与输出,并在删除FileRenamed.dat后运行。


我最近发现了IBuildUpToDateCheckProvider接口,它解决了这个问题。我不确定这是否是正确的方法。我不喜欢的是你必须扩展Visual Studio和MSBuild才能达到目标。 - alerosmile
https://dev59.com/EqDia4cB1Zd3GeqPILnW类似于这个问题。在一个VS项目中很简单的事情,在一个C#项目中似乎只能通过编写VS扩展来实现。 - alerosmile

0

看起来这只是文档不够完善。此网站显示您可以轻松连接命令行工具,同时提升tlog文件的增量功能。

为了确保信息不会丢失,我将复制他们的用例,但是看到那个,我认为很容易转换为您的需求。每个dcx的出现都可以被替换为例如data

1. 创建一个定义.xml文件

  • 定义一个ItemType
  • ContentType链接到ItemType
  • 连接一个FileExtension
<?xml version="1.0" encoding="utf-8"?>
<ProjectSchemaDefinitions xmlns="http://schemas.microsoft.com/build/2009/properties">
  <!-- Associate DXCShader item type with .hlsl files -->
  <ItemType Name="DXCShader" DisplayName="DXC Shader" />
  <ContentType Name="DXCShader" ItemType="DXCShader" DisplayName="DXC Shader" />
  <FileExtension Name=".hlsl" ContentType="DXCShader" />
</ProjectSchemaDefinitions>

2. 创建一个.targets文件

  • 包含.xml定义文件
  • 创建一个Target,该目标依赖于您的构建挂钩之一(这里是ClCompile
  • 在您的目标中创建一个ItemGroup,它将用作CustomBuild的参数。MessageCommandAdditionalInputsOutput是相关的元属性。
  • 使用MinimalRebuildFromTracking="true"和一个TrackerLogDirectory来调用CustomBuild以包含tlog文件。 这部分是使MSBuild跳过构建的魔法成分,如果您的依赖项是最新的。
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <!-- Include definitions from dxc.xml, which defines the DXCShader item. -->
    <PropertyPageSchema Include="$(MSBuildThisFileDirectory)dxc.xml" />
    <!-- Hook up DXCShader items to be built by the DXC target. -->
    <AvailableItemName Include="DXCShader">
      <Targets>DXC</Targets>
    </AvailableItemName>
  </ItemGroup>

  <Target
    Name="DXC"
    Condition="'@(DXCShader)' != ''"
    BeforeTargets="ClCompile">

    <Message Importance="High" Text="Building shaders!!!" />

    <!-- Find all shader headers (.hlsli files) -->
    <ItemGroup>
      <ShaderHeader Include="*.hlsli" />
    </ItemGroup>
    <PropertyGroup>
      <ShaderHeaders>@(ShaderHeader)</ShaderHeaders>
    </PropertyGroup>

    <!-- Setup metadata for custom build tool -->
    <ItemGroup>
      <DXCShader>
        <Message>%(Filename)%(Extension)</Message>
        <Command>
          "$(WDKBinRoot)\x86\dxc.exe" -T vs_6_0 -E vs_main %(Identity) -Fh %(Filename).vs.h -Vn %(Filename)_vs
          "$(WDKBinRoot)\x86\dxc.exe" -T ps_6_0 -E ps_main %(Identity) -Fh %(Filename).ps.h -Vn %(Filename)_ps
        </Command>
        <AdditionalInputs>$(ShaderHeaders)</AdditionalInputs>
        <Outputs>%(Filename).vs.h;%(Filename).ps.h</Outputs>
      </DXCShader>
    </ItemGroup>

    <!-- Compile by forwarding to the Custom Build Tool infrastructure,
         so it will take care of .tlogs and error/warning parsing -->
    <CustomBuild
      Sources="@(DXCShader)"
      MinimalRebuildFromTracking="true"
      TrackerLogDirectory="$(TLogLocation)"
      ErrorListRegex="(?'FILENAME'.+):(?'LINE'\d+):(?'COLUMN'\d+): (?'CATEGORY'error|warning): (?'TEXT'.*)" />
  </Target>
</Project>

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