asp.net-core.net-coremsbuildincremental-build

Incremental build in .NET Core & Visual Studio


This toy project doesn't have working incremental build:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <Timestamp>$([System.DateTime]::Now.ToString("yyyy-MM-dd\THHmmss"))</Timestamp>
  </PropertyGroup>

  <ItemGroup>
    <MyInputs Include="myinput.txt" />
    <MyOutput Include="myoutput.txt" />
  </ItemGroup>

  <Target
    Name="test_BeforeTargets_BeforeBuild"
    BeforeTargets="BeforeBuild"
    Inputs="@(MyInputs)"
    Outputs="@(MyOutput)"
  >
    <Message Text="test_BeforeTargets_BeforeBuild" Importance="high" />
    <WriteLinestoFile File="myoutput.txt" Lines="$(Timestamp)" Overwrite="true" />
  </Target>
</Project>

There are 3 build outcomes that I've noticed in Visual Studio:

  1. When myinput.txt and the .csproj files are modified I get my desired outcome:

    1>Target "test_BeforeTargets_BeforeBuild" in file "C:\Users\dan\source\repos\TestMSBuild\TestMSBuild.csproj":
    1>  Building target "test_BeforeTargets_BeforeBuild" completely.
    1>  Input file "myinput.txt" is newer than output file "myoutput.txt".
    1>  Task "Message"
    1>    Task Parameter:Text=test_BeforeTargets_BeforeBuild
    1>    Task Parameter:Importance=high
    1>    test_BeforeTargets_BeforeBuild
    1>  Done executing task "Message".
    1>  Task "WriteLinestoFile"
    1>    Task Parameter:File=myoutput.txt
    1>    Task Parameter:Lines=2019-02-21T163909
    1>    Task Parameter:Overwrite=True
    1>  Done executing task "WriteLinestoFile".
    1>Done building target "test_BeforeTargets_BeforeBuild" in project "TestMSBuild.csproj".
    
  2. When myinput.txt is not modified, but the .csproj is. I get the expected skipping of the target:

    1>Target "test_BeforeTargets_BeforeBuild" in file "C:\Users\dan\source\repos\TestMSBuild\TestMSBuild.csproj":
    1>  Skipping target "test_BeforeTargets_BeforeBuild" because all output files are up-to-date with respect to the input files.
    1>  Input files: myinput.txt
    1>  Output files: myoutput.txt
    1>Done building target "test_BeforeTargets_BeforeBuild" in project "TestMSBuild.csproj".
    
  3. If I build twice without modifying anything, then modify only myinput.txt, I get a one-liner output, no matter how many times I try to build:

    ========== Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========
    

I expected that when I modify myinput.txt only, the target will not be skipped. Currently, this is only true if I also modify the .csproj file at the same time.

The following variations of BeforeTargets & AfterTargets were tried but to no avail:

BeforeTargets="BeforeBuild" (as above)
BeforeTargets="Build"
AfterTargets="BeforeBuild"
AfterTargets="Build"

I've only tried .NET-Core (Microsoft.NET.Sdk) and ASP.NET-Core (Microsoft.NET.Sdk.Razor & Microsoft.NET.Sdk.Web) projects but both have the same result.

Question:

How can I get this toy project working?


Solution

  • While the MSBuild-based up-to-date check works as expected, there is also an additional up-to-date check performed Visual Studio to determine if it should call MSBuild or not. Since it doesn't know about your input file, it determines that there is no need to run MSBuild on that project.

    This can be changed by telling VS about it using the UpToDateCheckInput and UpToDateCheckOutput items that the project system uses for this check:

    <ItemGroup>
      <UpToDateCheckInput Include="@(MyInputs)" />
      <UpToDateCheckOutput Include="@(MyOutputs)" />
    </ItemGroup>