Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Visual Studio determine whether it has to start MSBuild or not?

I've done a lot of MSBuild customization for my C++ projects in the past. The Input and Output attributes of a MSBuild target are used to determine whether the target has to be executed or not. Additionally Visual Studio uses the .tlog files (located in the intermediate directory) to determine whether MSBuild has to be invoked at all.

Now I'm working on a C# project. I wrote a simple MSBuild target which copies a file to the output directory:

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

The target works as expected if the build is invoked through MSBuild.exe. The file is copied if the target file does not exist or the source file has been modified.

If I invoke the build inside Visual Studio it does not work as expected. Visual Studio does not invoke MSBuild if I delete the file from the output directory. On the other hand MSBuild is invoked every time I build the project after modifiying the source file even if there are no other changes made.

It seems that Visual Studio just compares every file from a project to the output files (.exe, .dll or .pdb). If any file in the project is newer than the output files, MSBuild is invoked. In my case MSBuild does not update the .exe file, so MSBuild is invoked again and again.

In a C++ project this behaviour is controlled by the .tlog files. Is there anything similar in a C# project?

Thanks a lot!

like image 233
alerosmile Avatar asked Dec 14 '25 15:12

alerosmile


1 Answers

It looks like this is just poorly documented. This site shows you can easily hook up a command line tool, while lifting on the incremental features of tlog files.

To make sure the information doesn't get lost, I'll just copy over their use case, but looking at that, I think it's easy to transform into your needs. Every occurrence of dcx can be replaced by e.g. data

1. Create a definition .xml file

  • Define an ItemType
  • Link a ContentType to the ItemType
  • Hook up a 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. Create a .targets file

  • Include the .xml definitions file
  • Create a Target that depends on one of your build hooks (here: ClCompile)
  • Create an ItemGroup in your Target that will serve as the argument to your CustomBuild. Message, Command, AdditionalInputs and Output are meta-attributes that are relevant.
  • Invoke CustomBuild with MinimalRebuildFromTracking="true" and a TrackerLogDirectory to contain the tlog files. This part is the magic ingredient that makes MSBuild skip the build if your dependencies are up-to-date.
<?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>
like image 111
Grimace of Despair Avatar answered Dec 16 '25 05:12

Grimace of Despair



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!