Search code examples
visual-studiomsbuildmicrosoft.codeanalysis

Visual Studio repeatedly rebuilds projects after code analyser is updated


This relates to Visual Studio 2017 Community. Most of our build process prefers the VS2015 tools, but has been updated to build under VS2017 tools too.

  • Say we have a project MyProject.csproj which references a code analyser binary MyAnalyzer.dll.
  • MyProject is a fairly heavily-referenced library in our solution (~100 projects in the tree) and it changes very rarely.
  • So the time comes to add new rule to MyAnalyzer. We put the new version in the repo, overwriting the old.
  • Suddenly, MyProject and everything which depends upon it is being rebuilt every time!

The diagnostic message provided helpfully by Visual Studio:

Project 'MyProject' is not up to date. Input file '..\codeanalysis\bin\myanalyzer.dll' is modified after output file ''.

Building MyProject on its own exhibits this behaviour, every time. But after forcing a Rebuild of MyProject and letting the rest of the projects sort themselves out, the problem goes away.

Until the next time we update MyAnalyzer, of course.

  • Visual Studio considers the modification times of a project's code analyser libraries when determining if a build is necessary.
  • The build of the project will bail out early, having detected that no build is necessary. The output files will of course not have their timestamps updated.
  • Therefore Visual Studio will build the project every time, in addition to everything which depends upon it, only to have MSBuild say 'nope, nothing to do' about a hundred times.
  • For a 100-project solution this can cause a regular build to take nearly a minute, when it should only need to spend a few seconds building the things which have actually changed.

MSBuild is well aware that nothing has changed. Is there any way to tell this to Visual Studio? I am aware that technically VS is correct here, but I know things it doesn't.


Solution

  • Ultimately it looked like I was indeed doing the wrong thing here. Trying to suppress VS's change detection is incorrect and what I should be doing is forcing MSBuild to rebuild when the analyser changes.

    So basically, make MSBuild's change detection behaviour match VS's instead of the other way around.

    I accomplished this by adding the analyser assemblies as hidden, dummy content items to each project, so my common ItemGroup now looks like:

    <ItemGroup>
      <Analyzer Include="@(AnalyzerAssemblies)" />
      <Content Include="@(AnalyzerAssemblies)">
        <Private>False</Private>
        <Visible>False</Visible>
      </Content>
    </ItemGroup>
    

    This appears to have the desired result, causing MSBuild to consider the analyser timestamp when VS invokes it, which makes the entire solution rebuild once and then settle down.