I have a custom MSBuild target with `AfterTargets="Build"` which is rewriting certain assembly references for assemblies in the build output folder of net framework applications. The aim is to roll this out across my company to help fix assembly loading problems for 3rd party packages who have changed the private keys they use to sign their packages.
I have set this up with Input and Output for incremental builds as suggested here: https://www.jetbrains.com/help/resharper/Building_Solution.html#limitations
This all works fine when there is an actual build. The problem is when building again (with no changes) using ReSharper build (I'm using Rider for this but it's the same in Visual Studio). In this case it is reported that nothing happens in the build (as would be expected), with the build output as follows
Build with surface heuristics started at 16:10:40
Use build tool: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\MSBuild.exe
CONSOLE: Microsoft (R) Build Engine version 16.0.461+g6ff56ef63c for .NET Framework
CONSOLE: Copyright (C) Microsoft Corporation. All rights reserved.
CONSOLE: Build started 18/02/2020 16:10:40.
Build completed in 00:00:00.671
Build succeeded at 16:10:40
Even when writing a diagnostic-level msbuild log to file, all I get is a load of environment variables followed by just
Time Elapsed 00:00:00.12
Since the Build target isn't being run, my custom `AfterTargets="Build"` target doesn't run either, regardless of whether its Inputs have changed (all as expected).
However, msbuild is changing the dlls in the output directory.
First build: The application references a package Foo, and Foo.dll is copied into its output directory during the build. My target runs and updates the assembly references in Foo.dll. All is well.
Second build with no changes: All logs suggest nothing happens, but Foo.dll is copied from my global NuGet cache into my build output folder, clobbering the changes that were made to it by my custom target.
I have used procmon to verify that it is the msbuild instance launched by resharper which is doing this. The msbuild instance is called on a dummy project file in AppData\Local\Temp\8\ which looks like:
<UsingTask AssemblyFile='C:\Program Files\JetBrains\Rider 2019.3.1\lib\ReSharperHost\JetBrains.Platform.MsBuildTask.v15.dll' TaskName='JetBrains.Platform.MsBuildTask.ControllerTask' />
<ControllerTask PortValue='64263' BuildToolVersion='15.9' LoggerPath='' />
I have no idea what this is up to. Changing my custom target to use `AfterTargets="ControllerTaskTarget"` still does nothing. Since I can't see any other targets being run by msbuild here I haven't got anything to hook my custom target onto to achieve the desired behaviour.
Any idea what I can do here?
Some options I have already explored:
- Using incremental build Inputs and Outputs on my target as recommended - not a starter since there are no targets being run in the first place so my target never runs.
- Updating timestamps of the dlls when I edit them so they look like they haven't been edited. This works a bit - if it's a dll from a project reference then this "fools" resharper build into thinking it hasn't changed, the dependency isn't rebuilt and the dll isn't copy-localed again so I get to keep my edited one. It doesn't work for dlls from package references though. MSBuild seems to be using "ChangeTime" on the file instead of "LastWriteTime" to look for changes, which as far as I can see I am unable to update when running as a normal user.
- Disabling ReSharper build. Works fine, but we don't get ReSharper build... Not a starter since lots of devs in the company use ReSharper build and like/need the perf benefits of it.
- Ticking "Invoke BeforeBuild and AfterBuild targets for skipped projects" in ReSharper build options. This does work, but is a real last resort - lots of devs are building huge solutions and forcing them to do work (which is not necessarily this one target) for every skipped project on every build is not likely to be an acceptable workflow.