msbuildvisual-studio-2012incremental-build

Visual Studio 2012 - MSBuild incremental build not detecting changes


I have customised an MSBuild project so that the default target is a new target named similarly to 'BuildWithExternalReference'. This new target calls two other targets; the first is a custom target called something like 'BuildExternalReference' which builds a DLL using an external tool. The DLL that is built is a reference for the main project, which is built using the normal 'Build' target. I have setup the Inputs and Outputs attributes for the 'BuildExternalReference' target so the Inputs reference the source files and the outputs reference the resulting DLL.

In both Visual Studio 2012 and Visual Studio 2010 the build works correctly the first time it is invoked. However, on subsequent builds if I change the external source files (referenced by the 'BuildExternalReference' target Inputs attribute) then Visual Studio 2012 simply reports 'Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped'. Visual Studio 2010 continues to work perfectly. In addition, building from the command line with MSBuild.exe works perfectly.

I'm aware that the build system in Visual Studio 2012 has changed, but I can't find information about changes to the way incremental builds are performed.

Has anything changed in Visual Studio 2012 to cause incremental builds to change?

Here's a cut down version of the csproj file I'm using:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="BuildWithExternalTool" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <ExternalSourceFiles Include="..\ExternalSourceFiles\\*\*.cs" />
    <ExternalDll Include="..\ExternalSource\External.dll" />
  </ItemGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  <Target Name="BuildExternalTool" Inputs="@(ExternalSourceFiles);" Outputs="@(ExternalDll)">
    <Exec Command="C:\External\Path\To\Tool.exe" />
  </Target>
  <Target Name="BuildWithExternalTool">
    <CallTarget Targets="BuildExternalTool" />
    <CallTarget Targets="Build" />
  </Target>
</Project>

Update 1st of November 2012

Here's a complete self contained example which reproduces the issue:

https://skydrive.live.com/redir?resid=EA1DD6ACA92F9EFF!155&authkey=!ANhuqF_rrCgxpLE

This is a solution with one project. The MSBuildIssueExample\MSBuildIssueExample.csproj file has been customised so there is a custom default target. This default target calls a custom target (called 'ExternalTool') and then the default Build target.

The custom ExternalTool target writes out some messages to make sure it's working, and also copies the contents of the MSBuildIssueExample\ExternalTool\Input.txt file over the MSBuildIssueExample\ExternalTool\Output.txt file.

The Input.txt file is a input of the ExternalTool target, and Output.txt is an output.

To recreate the issue follow these steps:

1) Open the solution in the designated version of Visual Studio

2) Build the solution once to make sure the outputs are up to date with respect to the inputs

3) Modify MSBuildIssueExample\ExternalTool\Input.txt so its content does not match Output.txt

4) Build again

When you go through this process in Visual Studio 2010 the ExternalTool target will be invoked again, and the Input.txt file will be copied over Output.txt.

When you go through this process in Visual Studio 2012 the ExternalTool target will not be invoked, even though the inputs are newer than the outputs, and as a result the contents of Input.txt will not be written to Output.txt.

However, if you do Rebuild (rather than just Build) then both versions of Visual Studio work as expected.


Solution

  • This feedback from Microsoft answers the question:

    This is due to a change in VS 2012 where C#/VB projects now do a "fast up-to-date check" that allows them to skip the build, rather than forcing the build all the time. One downside, however, is that fast up-to-date check does not take into account custom targets, which is why your incremental change was not detected. If you wish to disable the "fast up-to-date check" please set "DISABLEFASTUPTODATECHECK" to true either as an MSBuild property in the project file or as an environment variable in the environment you launch VS from.

    So basically this is a breaking change in Visual Studio 2012 that unfortunately does not seem to be documented very well.