From b9a2259c1eae97a937cdc89ae938a9a15ff05c33 Mon Sep 17 00:00:00 2001 From: Drew Noakes Date: Wed, 28 Jun 2023 22:39:12 +1000 Subject: [PATCH] Fix FUTD check of copy items when building for debug When the user presses F5 the Solution Build Manager (SBM) calls the Fast Up-to-date Check (FUTDC) twice, for some reason I don't fully understand. If you invoke build directly, it's only called once. In the debug case, the first of the two calls occurs *before* any SBM events are fired, which breaks an assumption that was made about the sequence of events. Specifically we assumed that the FUTDC would only be called between calls to `IVsUpdateSolutionEvents.UpdateSolution_StartUpdate` and `IVsUpdateSolutionEvents.UpdateSolution_Done`. But that's not the case when the user presses F5. It turns out that this surfaces an issue only for `CopyToOutputDirectory` items, as its only for these items that we use the shared `SolutionBuildContext` cache of timestamps. That cache helps considerably with the performance of Build Acceleration across the solution, where the same files are checked repeatedly within the solution. This change fixes the issue by creating a fake "solution build starting" event when we detect that no such event occurred prior to the FUTDC being called. --- .../UpToDate/BuildUpToDateCheck.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UpToDate/BuildUpToDateCheck.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UpToDate/BuildUpToDateCheck.cs index 47b152a67c5..087ad6525d9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UpToDate/BuildUpToDateCheck.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UpToDate/BuildUpToDateCheck.cs @@ -726,12 +726,7 @@ private bool CheckCopyToOutputDirectoryItems(Log log, UpToDateCheckImplicitConfi { ITimestampCache? timestampCache = _solutionBuildContextProvider.CurrentSolutionBuildContext?.CopyItemTimestamps; - if (timestampCache is null) - { - // This might be null during validation runs which can start after the last project build. - // If it is, we will just skip checking the copy items. - return true; - } + Assumes.NotNull(timestampCache); string outputFullPath = Path.Combine(state.MSBuildProjectDirectory, state.OutputRelativeOrFullPath); @@ -965,6 +960,25 @@ private async Task IsUpToDateInternalAsync( // Short-lived cache of timestamp by path var timestampCache = new TimestampCache(_fileSystem); + // Ensure we have a context object for the current solution build. + // + // Ordinarily, this is created when the SBM calls ISolutionBuildEventListener.NotifySolutionBuildStarting, + // and cleared again later when the SBM calls ISolutionBuildEventListener.NotifySolutionBuildCompleted. + // + // However there are two cases where it may be null here: + // + // 1. When performing a validation run that continues after the solution build completed, or + // 2. When the build occurs in response to debugging (e.g. F5) in which case the SBM calls the + // FUTDC *before* it invokes any solution build events. + // + // In either case, we construct an event here lazily so that we can correctly test for the + // existence of copy items in CheckCopyToOutputDirectoryItems. + if (_solutionBuildContextProvider.CurrentSolutionBuildContext is null) + { + _solutionBuildEventListener.NotifySolutionBuildStarting(); + Assumes.NotNull(_solutionBuildContextProvider.CurrentSolutionBuildContext); + } + globalProperties.TryGetValue(FastUpToDateCheckIgnoresKindsGlobalPropertyName, out string? ignoreKindsString); (LogLevel requestedLogLevel, Guid projectGuid) = await (