How to avoid that Visual Studio incremental build does not run when files outside <Compile> and <EmbeddedResource> are changed?
Asked Answered
H

2

8

I have a VS2017 csharp project and the .csproj file looks like the following:

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

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp2.0</TargetFramework>
    </PropertyGroup>
  
    <ItemGroup>
        <MyItem Include="file.dat" />
    </ItemGroup>

    <PropertyGroup>
        <PrepareResourcesDependsOn>
            $(PrepareResourcesDependsOn);
            MyCompileTarget
         </PrepareResourcesDependsOn>
        <CoreCompileDependsOn>
            $(CoreCompileDependsOn);
            MyCompileTarget
        </CoreCompileDependsOn>
    </PropertyGroup>

    <Target Name="MyCompileTarget" Inputs="@(MyItem)" Outputs="@(MyItem->'%(FileName).out')">
    ...
    </Target>
 </Project>

Where MyCompileTarget is a target that generates the file.out from file.dat (in the actual code the incremental build target and properties are in a target file automatically included via a NuGet package).

The issue is that if I change file.dat and press on Build, no target is executed at all, (but MyTarget is correctly executed with Rebuild or when running with msbuild). I would expect the MyCompileTarget to be executed so that the file.out file is updated.

The same issue occurs if I use BeforeBuild or AfterBuild instead of PrepareResourcesDependsOn etc.

It seems that Visual Studio incremental build won't start unless some file in @(Compile) or @(EmbeddedResource) is changed. Indeed, if I add the following

<EmbeddedResource>file.dat</EmbeddedResource>

the incremental build works as expected (but clearly I do not want to embeed the file.dat into the generated assembly).

Is it possible to force Visual Studio to enable incremental build if file.dat is modified, and if the corresponding generated file is older than file.dat or it does not exist?

Note: the same issue occurs using VS2015, with .NET CORE or .NET FRAMEWORK. Also, incremental build will be triggered if I change a csharp file, and it will therefore trigger MyTask, but only if file.dat is newer than the generated file (as expected).

Thanks in advance, Fabio.

Hemangioma answered 25/4, 2018 at 15:43 Comment(2)
If you run this with verbose output, does it say why the target gets skipped? What might help is adding a record to the file tracker mechanism in your target, something like <WriteLinesToFile File="$(TLogLocation)$(ProjectName).write.u.tlog" Lines="@(MyItem->'%(FileName).out"/>Gabo
Thanks for the reply. I tried your solution but still didn't work. When I change the source file it still won't recompile, unless I delete the output file. Is there a way to instert the file in some list so that the 'fast-up-to-date' in Visual Studio will catch the change (difference between source and output file)?Hemangioma
F
10

Is it possible to force Visual Studio to enable incremental build if file.dat is modified

You can set the property DisableFastUpToDateCheck to true in the project file to disable FastUpToDateCheck for Visual Studio build manager:

<PropertyGroup>
    <DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
</PropertyGroup>

Check MSDN about DisableFastUpToDateCheck:

A boolean value that applies to Visual Studio only. The Visual Studio build manager uses a process called FastUpToDateCheck to determine whether a project must be rebuilt to be up to date. This process is faster than using MSBuild to determine this. Setting the DisableFastUpToDateCheck property to true lets you bypass the Visual Studio build manager and force it to use MSBuild to determine whether the project is up to date

Update:

Also, we can set the UpToDateCheckInput to the item:

<UpToDateCheckInput Include="file.dat" />
Freed answered 26/4, 2018 at 9:56 Comment(7)
Thanks a lot for the reply :) Is there a way to do that without disabling the fast-up-to-date-check? I tried this <UpToDateCheckInput Include="file.dat" /> but it won't work.Hemangioma
The issue is that I need to keep fast up to date checks enabled for performance reasons, and the solution above (UpToDateCheckInput) won't work, at least not when the item is in a .target file, which is automatically included in VS2017 projects via NuGet package reference.Hemangioma
@FabioStrocco, Not sure I understand you clear. You should add <DisableFastUpToDateCheck>True</DisableFastUpToDateCheck> in the .csproj file under the line <TargetFramework>netcoreapp2.0</TargetFramework> not in the .target file in the nuget package. Besides, if you want keep build performance and the file could not be set file in @(Compile) or @(EmbeddedResource), I am afraid there is no better way to solve this problem.Freed
My fault, I placed the item group in the wrong place. <UpToDateCheckInput Include="file.dat" /> solves the issue while still keeping fast-up-to-date-check enabled, so I suggest you include it in your answer so I can close the topic.Hemangioma
What I am saying is that it seems to work when I add the following under ItemGroup: <UpToDateCheckInput Include="file.dat" />, even whith fast-track-update-check set to true. Apparently it will now include file.dat in the fast-up-to-date checks, therefore triggering the build when file.dat changes (it will then be my build script that decides which targets to run). Does it make sense to you? I found this post below. #50026855Hemangioma
The reason why I want to place all this into a NuGet target file is that I want to distribute a compiler as a NuGet package, which will automatically trigger compilation on build, without the NuGet package user has to include anything else than MyItem in its .csproj file.Hemangioma
You should never have to disable the up to date check. This is bad advice, and I suggest deleting or editing this answer. People will copy/paste that property to their projects and suffer through slow builds.Certified
C
2

Disabling the VS fast up-to-date check will make your builds much slower. Don't do it!

Instead, make sure the up-to-date check knows about the items in your project and how they relate to build. There are two kinds of item you can add to your project for this:

  • UpToDateCheckInput for inputs
  • UpToDateCheckBuilt for outputs

In your case you need the second option as there is both an input and an output. You need to ensure that if you delete the output, it is rebuilt.

<ItemGroup>
  <UpToDateCheckBuilt Original="@(MyItem)" Include="@(MyItem->'%(FileName).out')">
</ItemGroup>

For more information, see the documentation:

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

Certified answered 11/10, 2021 at 21:39 Comment(1)
UpToDateCheckBuilt is not a property. Should this be in an ItemGroup maybe?Treblinka

© 2022 - 2024 — McMap. All rights reserved.