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 (