diff --git a/src/BuiltInTools/DotNetWatchTasks/DotNetWatchTasks.csproj b/src/BuiltInTools/DotNetWatchTasks/DotNetWatchTasks.csproj
index aee7ab0bd9a3..af178129da01 100644
--- a/src/BuiltInTools/DotNetWatchTasks/DotNetWatchTasks.csproj
+++ b/src/BuiltInTools/DotNetWatchTasks/DotNetWatchTasks.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/src/BuiltInTools/dotnet-watch/Internal/BrowserSpecificReporter.cs b/src/BuiltInTools/dotnet-watch/Browser/BrowserSpecificReporter.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/BrowserSpecificReporter.cs
rename to src/BuiltInTools/dotnet-watch/Browser/BrowserSpecificReporter.cs
diff --git a/src/BuiltInTools/dotnet-watch/DotNetWatch.targets b/src/BuiltInTools/dotnet-watch/Build/DotNetWatch.targets
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/DotNetWatch.targets
rename to src/BuiltInTools/dotnet-watch/Build/DotNetWatch.targets
diff --git a/src/BuiltInTools/dotnet-watch/EvaluationResult.cs b/src/BuiltInTools/dotnet-watch/Build/EvaluationResult.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/EvaluationResult.cs
rename to src/BuiltInTools/dotnet-watch/Build/EvaluationResult.cs
diff --git a/src/BuiltInTools/dotnet-watch/FileItem.cs b/src/BuiltInTools/dotnet-watch/Build/FileItem.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/FileItem.cs
rename to src/BuiltInTools/dotnet-watch/Build/FileItem.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/FilePathExclusions.cs b/src/BuiltInTools/dotnet-watch/Build/FilePathExclusions.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/FilePathExclusions.cs
rename to src/BuiltInTools/dotnet-watch/Build/FilePathExclusions.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/MSBuildFileSetResult.cs b/src/BuiltInTools/dotnet-watch/Build/MSBuildFileSetResult.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/MSBuildFileSetResult.cs
rename to src/BuiltInTools/dotnet-watch/Build/MSBuildFileSetResult.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs b/src/BuiltInTools/dotnet-watch/Build/MsBuildFileSetFactory.cs
similarity index 98%
rename from src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs
rename to src/BuiltInTools/dotnet-watch/Build/MsBuildFileSetFactory.cs
index 166a2d19b08c..008634208eee 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs
+++ b/src/BuiltInTools/dotnet-watch/Build/MsBuildFileSetFactory.cs
@@ -68,7 +68,7 @@ internal class MSBuildFileSetFactory(
reporter.Output($"MSBuild output from target '{TargetName}':");
}
- BuildUtilities.ReportBuildOutput(reporter, capturedOutput, success, projectDisplay: null);
+ BuildOutput.ReportBuildOutput(reporter, capturedOutput, success, projectDisplay: null);
if (!success)
{
return null;
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/ProjectNodeMap.cs b/src/BuiltInTools/dotnet-watch/Build/ProjectNodeMap.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/ProjectNodeMap.cs
rename to src/BuiltInTools/dotnet-watch/Build/ProjectNodeMap.cs
diff --git a/src/BuiltInTools/dotnet-watch/CommandLineOptions.cs b/src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/CommandLineOptions.cs
rename to src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs
diff --git a/src/BuiltInTools/dotnet-watch/DotNetWatchContext.cs b/src/BuiltInTools/dotnet-watch/CommandLine/DotNetWatchContext.cs
similarity index 66%
rename from src/BuiltInTools/dotnet-watch/DotNetWatchContext.cs
rename to src/BuiltInTools/dotnet-watch/CommandLine/DotNetWatchContext.cs
index 53dd2f99dbde..d0f597d134f8 100644
--- a/src/BuiltInTools/dotnet-watch/DotNetWatchContext.cs
+++ b/src/BuiltInTools/dotnet-watch/CommandLine/DotNetWatchContext.cs
@@ -12,5 +12,13 @@ internal sealed class DotNetWatchContext
public required ProcessRunner ProcessRunner { get; init; }
public required ProjectOptions RootProjectOptions { get; init; }
+
+ public MSBuildFileSetFactory CreateMSBuildFileSetFactory()
+ => new(
+ RootProjectOptions.ProjectPath,
+ RootProjectOptions.BuildArguments,
+ EnvironmentOptions,
+ ProcessRunner,
+ Reporter);
}
}
diff --git a/src/BuiltInTools/dotnet-watch/EnvironmentOptions.cs b/src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentOptions.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/EnvironmentOptions.cs
rename to src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentOptions.cs
diff --git a/src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs b/src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentVariables.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs
rename to src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentVariables.cs
diff --git a/src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs b/src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentVariablesBuilder.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs
rename to src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentVariablesBuilder.cs
diff --git a/src/BuiltInTools/dotnet-watch/GlobalOptions.cs b/src/BuiltInTools/dotnet-watch/CommandLine/GlobalOptions.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/GlobalOptions.cs
rename to src/BuiltInTools/dotnet-watch/CommandLine/GlobalOptions.cs
diff --git a/src/BuiltInTools/dotnet-watch/ProjectOptions.cs b/src/BuiltInTools/dotnet-watch/CommandLine/ProjectOptions.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/ProjectOptions.cs
rename to src/BuiltInTools/dotnet-watch/CommandLine/ProjectOptions.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/FileWatcher/ChangeKind.cs b/src/BuiltInTools/dotnet-watch/FileWatcher/ChangeKind.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/FileWatcher/ChangeKind.cs
rename to src/BuiltInTools/dotnet-watch/FileWatcher/ChangeKind.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/FileWatcher/DirectoryWatcher.cs b/src/BuiltInTools/dotnet-watch/FileWatcher/DirectoryWatcher.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/FileWatcher/DirectoryWatcher.cs
rename to src/BuiltInTools/dotnet-watch/FileWatcher/DirectoryWatcher.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/FileWatcher/EventBasedDirectoryWatcher.cs b/src/BuiltInTools/dotnet-watch/FileWatcher/EventBasedDirectoryWatcher.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/FileWatcher/EventBasedDirectoryWatcher.cs
rename to src/BuiltInTools/dotnet-watch/FileWatcher/EventBasedDirectoryWatcher.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/FileWatcher.cs b/src/BuiltInTools/dotnet-watch/FileWatcher/FileWatcher.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/FileWatcher.cs
rename to src/BuiltInTools/dotnet-watch/FileWatcher/FileWatcher.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/FileWatcher/PollingDirectoryWatcher.cs b/src/BuiltInTools/dotnet-watch/FileWatcher/PollingDirectoryWatcher.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/FileWatcher/PollingDirectoryWatcher.cs
rename to src/BuiltInTools/dotnet-watch/FileWatcher/PollingDirectoryWatcher.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyAppModel.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyAppModel.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyAppModel.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyDeltaApplier.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyDeltaApplier.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyHostedAppModel.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedAppModel.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyHostedAppModel.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyHostedDeltaApplier.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/AppModels/BlazorWebAssemblyHostedDeltaApplier.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/DefaultAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DefaultAppModel.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/DefaultAppModel.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/AppModels/DefaultAppModel.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DefaultDeltaApplier.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/AppModels/DefaultDeltaApplier.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/DeltaApplier.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/AppModels/DeltaApplier.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadAppModel.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/HotReloadAppModel.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/HotReloadAppModel.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/AppModels/HotReloadAppModel.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/SingleProcessDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/AppModels/SingleProcessDeltaApplier.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/SingleProcessDeltaApplier.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/AppModels/SingleProcessDeltaApplier.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs
similarity index 88%
rename from src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
rename to src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs
index fafecf41b950..e958d41308d3 100644
--- a/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs
@@ -3,20 +3,24 @@
using System.Collections.Immutable;
using System.Diagnostics;
-using Microsoft.Build.Graph;
using Microsoft.CodeAnalysis;
namespace Microsoft.DotNet.Watch
{
- internal sealed partial class HotReloadDotNetWatcher : Watcher
+ internal sealed class HotReloadDotNetWatcher
{
private readonly IConsole _console;
private readonly IRuntimeProcessLauncherFactory? _runtimeProcessLauncherFactory;
private readonly RestartPrompt? _rudeEditRestartPrompt;
- public HotReloadDotNetWatcher(DotNetWatchContext context, IConsole console, MSBuildFileSetFactory fileSetFactory, IRuntimeProcessLauncherFactory? runtimeProcessLauncherFactory)
- : base(context, fileSetFactory)
+ private readonly DotNetWatchContext _context;
+ private readonly MSBuildFileSetFactory _fileSetFactory;
+
+ public HotReloadDotNetWatcher(DotNetWatchContext context, IConsole console, IRuntimeProcessLauncherFactory? runtimeProcessLauncherFactory)
{
+ _context = context;
+ _fileSetFactory = context.CreateMSBuildFileSetFactory();
+
_console = console;
_runtimeProcessLauncherFactory = runtimeProcessLauncherFactory;
if (!context.Options.NonInteractive)
@@ -33,32 +37,32 @@ public HotReloadDotNetWatcher(DotNetWatchContext context, IConsole console, MSBu
}
}
- public override async Task WatchAsync(CancellationToken shutdownCancellationToken)
+ public async Task WatchAsync(CancellationToken shutdownCancellationToken)
{
CancellationTokenSource? forceRestartCancellationSource = null;
var hotReloadEnabledMessage = "Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload.";
- if (!Context.Options.NonInteractive)
+ if (!_context.Options.NonInteractive)
{
- Context.Reporter.Output($"{hotReloadEnabledMessage}{Environment.NewLine} {(Context.EnvironmentOptions.SuppressEmojis ? string.Empty : "💡")} Press \"Ctrl + R\" to restart.", emoji: "🔥");
+ _context.Reporter.Output($"{hotReloadEnabledMessage}{Environment.NewLine} {(_context.EnvironmentOptions.SuppressEmojis ? string.Empty : "💡")} Press \"Ctrl + R\" to restart.", emoji: "🔥");
_console.KeyPressed += (key) =>
{
if (key.Modifiers.HasFlag(ConsoleModifiers.Control) && key.Key == ConsoleKey.R && forceRestartCancellationSource is { } source)
{
// provide immediate feedback to the user:
- Context.Reporter.Report(source.IsCancellationRequested ? MessageDescriptor.RestartInProgress : MessageDescriptor.RestartRequested);
+ _context.Reporter.Report(source.IsCancellationRequested ? MessageDescriptor.RestartInProgress : MessageDescriptor.RestartRequested);
source.Cancel();
}
};
}
else
{
- Context.Reporter.Output(hotReloadEnabledMessage, emoji: "🔥");
+ _context.Reporter.Output(hotReloadEnabledMessage, emoji: "🔥");
}
- await using var browserConnector = new BrowserConnector(Context);
- using var fileWatcher = new FileWatcher(Context.Reporter);
+ await using var browserConnector = new BrowserConnector(_context);
+ using var fileWatcher = new FileWatcher(_context.Reporter);
for (var iteration = 0; !shutdownCancellationToken.IsCancellationRequested; iteration++)
{
@@ -79,7 +83,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
try
{
- var rootProjectOptions = Context.RootProjectOptions;
+ var rootProjectOptions = _context.RootProjectOptions;
var runtimeProcessLauncherFactory = _runtimeProcessLauncherFactory;
// Evaluate the target to find out the set of files to watch.
@@ -96,14 +100,14 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
if (rootProjectCapabilities.Contains(AspireServiceFactory.AppHostProjectCapability))
{
runtimeProcessLauncherFactory ??= AspireServiceFactory.Instance;
- Context.Reporter.Verbose("Using Aspire process launcher.");
+ _context.Reporter.Verbose("Using Aspire process launcher.");
}
- var projectMap = new ProjectNodeMap(evaluationResult.ProjectGraph, Context.Reporter);
- compilationHandler = new CompilationHandler(Context.Reporter, Context.ProcessRunner);
- var scopedCssFileHandler = new ScopedCssFileHandler(Context.Reporter, projectMap, browserConnector);
- var projectLauncher = new ProjectLauncher(Context, projectMap, browserConnector, compilationHandler, iteration);
- evaluationResult.ItemExclusions.Report(Context.Reporter);
+ var projectMap = new ProjectNodeMap(evaluationResult.ProjectGraph, _context.Reporter);
+ compilationHandler = new CompilationHandler(_context.Reporter, _context.ProcessRunner);
+ var scopedCssFileHandler = new ScopedCssFileHandler(_context.Reporter, projectMap, browserConnector);
+ var projectLauncher = new ProjectLauncher(_context, projectMap, browserConnector, compilationHandler, iteration);
+ evaluationResult.ItemExclusions.Report(_context.Reporter);
var rootProjectNode = evaluationResult.ProjectGraph.GraphRoots.Single();
@@ -118,7 +122,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
}
var (buildSucceeded, buildOutput, _) = await BuildProjectAsync(rootProjectOptions.ProjectPath, rootProjectOptions.BuildArguments, iterationCancellationToken);
- BuildUtilities.ReportBuildOutput(Context.Reporter, buildOutput, buildSucceeded, projectDisplay: rootProjectOptions.ProjectPath);
+ BuildOutput.ReportBuildOutput(_context.Reporter, buildOutput, buildSucceeded, projectDisplay: rootProjectOptions.ProjectPath);
if (!buildSucceeded)
{
continue;
@@ -165,7 +169,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
return;
}
- await compilationHandler.Workspace.UpdateProjectConeAsync(RootFileSetFactory.RootProjectFile, iterationCancellationToken);
+ await compilationHandler.Workspace.UpdateProjectConeAsync(_fileSetFactory.RootProjectFile, iterationCancellationToken);
// Solution must be initialized after we load the solution but before we start watching for file changes to avoid race condition
// when the EnC session captures content of the file after the changes has already been made.
@@ -186,7 +190,7 @@ void FileChangedCallback(ChangedPath change)
{
if (AcceptChange(change, evaluationResult))
{
- Context.Reporter.Verbose($"File change: {change.Kind} '{change.Path}'.");
+ _context.Reporter.Verbose($"File change: {change.Kind} '{change.Path}'.");
ImmutableInterlocked.Update(ref changedFilesAccumulator, changedPaths => changedPaths.Add(change));
}
}
@@ -242,7 +246,7 @@ void FileChangedCallback(ChangedPath change)
if (!rootProjectCapabilities.Contains("SupportsHotReload"))
{
- Context.Reporter.Warn($"Project '{rootProject.GetDisplayName()}' does not support Hot Reload and must be rebuilt.");
+ _context.Reporter.Warn($"Project '{rootProject.GetDisplayName()}' does not support Hot Reload and must be rebuilt.");
// file change already detected
waitForFileChangeBeforeRestarting = false;
@@ -264,7 +268,7 @@ void FileChangedCallback(ChangedPath change)
HotReloadEventSource.Log.HotReloadStart(HotReloadEventSource.StartType.CompilationHandler);
var (projectsToRebuild, projectsToRestart) = await compilationHandler.HandleManagedCodeChangesAsync(
- autoRestart: Context.Options.NonInteractive || _rudeEditRestartPrompt?.AutoRestartPreference is true,
+ autoRestart: _context.Options.NonInteractive || _rudeEditRestartPrompt?.AutoRestartPreference is true,
restartPrompt: async (projectNames, cancellationToken) =>
{
if (_rudeEditRestartPrompt != null)
@@ -279,11 +283,11 @@ void FileChangedCallback(ChangedPath change)
}
else
{
- Context.Reporter.Output("Affected projects:");
+ _context.Reporter.Output("Affected projects:");
foreach (var projectName in projectNames.OrderBy(n => n))
{
- Context.Reporter.Output(" " + projectName);
+ _context.Reporter.Output(" " + projectName);
}
question = "Do you want to restart these projects?";
@@ -292,11 +296,11 @@ void FileChangedCallback(ChangedPath change)
return await _rudeEditRestartPrompt.WaitForRestartConfirmationAsync(question, cancellationToken);
}
- Context.Reporter.Verbose("Restarting without prompt since dotnet-watch is running in non-interactive mode.");
+ _context.Reporter.Verbose("Restarting without prompt since dotnet-watch is running in non-interactive mode.");
foreach (var projectName in projectNames)
{
- Context.Reporter.Verbose($" Project to restart: '{projectName}'");
+ _context.Reporter.Verbose($" Project to restart: '{projectName}'");
}
return true;
@@ -332,7 +336,7 @@ void FileChangedCallback(ChangedPath change)
foreach (var (success, output, projectPath) in buildResults)
{
- BuildUtilities.ReportBuildOutput(Context.Reporter, output, success, projectPath);
+ BuildOutput.ReportBuildOutput(_context.Reporter, output, success, projectPath);
}
if (buildResults.All(result => result.success))
@@ -349,7 +353,7 @@ void FileChangedCallback(ChangedPath change)
_ = await fileWatcher.WaitForFileChangeAsync(
change => AcceptChange(change, evaluationResult),
- startedWatching: () => Context.Reporter.Report(MessageDescriptor.FixBuildError),
+ startedWatching: () => _context.Reporter.Report(MessageDescriptor.FixBuildError),
shutdownCancellationToken);
}
@@ -357,7 +361,7 @@ void FileChangedCallback(ChangedPath change)
// Apply them to the workspace.
_ = await CaptureChangedFilesSnapshot(projectsToRebuild);
- Context.Reporter.Report(MessageDescriptor.ProjectsRebuilt, projectsToRebuild.Count);
+ _context.Reporter.Report(MessageDescriptor.ProjectsRebuilt, projectsToRebuild.Count);
}
if (!projectsToRestart.IsEmpty)
@@ -382,10 +386,10 @@ await Task.WhenAll(
}))
.WaitAsync(shutdownCancellationToken);
- Context.Reporter.Report(MessageDescriptor.ProjectsRestarted, projectsToRestart.Length);
+ _context.Reporter.Report(MessageDescriptor.ProjectsRestarted, projectsToRestart.Length);
}
- Context.Reporter.Report(MessageDescriptor.HotReloadChangeHandled, stopwatch.ElapsedMilliseconds);
+ _context.Reporter.Report(MessageDescriptor.HotReloadChangeHandled, stopwatch.ElapsedMilliseconds);
async Task> CaptureChangedFilesSnapshot(ImmutableDictionary? rebuiltProjects)
{
@@ -434,7 +438,7 @@ async Task> CaptureChangedFilesSnapshot(ImmutableDict
if (evaluationRequired)
{
- Context.Reporter.Report(fileAdded ? MessageDescriptor.FileAdditionTriggeredReEvaluation : MessageDescriptor.ProjectChangeTriggeredReEvaluation);
+ _context.Reporter.Report(fileAdded ? MessageDescriptor.FileAdditionTriggeredReEvaluation : MessageDescriptor.ProjectChangeTriggeredReEvaluation);
// TODO: consider re-evaluating only affected projects instead of the whole graph.
evaluationResult = await EvaluateRootProjectAsync(iterationCancellationToken);
@@ -442,7 +446,7 @@ async Task> CaptureChangedFilesSnapshot(ImmutableDict
// additional directories may have been added:
evaluationResult.WatchFiles(fileWatcher);
- await compilationHandler.Workspace.UpdateProjectConeAsync(RootFileSetFactory.RootProjectFile, iterationCancellationToken);
+ await compilationHandler.Workspace.UpdateProjectConeAsync(_fileSetFactory.RootProjectFile, iterationCancellationToken);
if (shutdownCancellationToken.IsCancellationRequested)
{
@@ -454,7 +458,7 @@ async Task> CaptureChangedFilesSnapshot(ImmutableDict
changedFiles = [.. changedFiles
.Select(f => evaluationResult.Files.TryGetValue(f.Item.FilePath, out var evaluatedFile) ? f with { Item = evaluatedFile } : f)];
- Context.Reporter.Report(MessageDescriptor.ReEvaluationCompleted);
+ _context.Reporter.Report(MessageDescriptor.ReEvaluationCompleted);
}
if (rebuiltProjects != null)
@@ -558,17 +562,17 @@ private async ValueTask WaitForFileChangeBeforeRestarting(FileWatcher fileWatche
_ = await fileWatcher.WaitForFileChangeAsync(
evaluationResult.Files,
- startedWatching: () => Context.Reporter.Report(MessageDescriptor.WaitingForFileChangeBeforeRestarting),
+ startedWatching: () => _context.Reporter.Report(MessageDescriptor.WaitingForFileChangeBeforeRestarting),
cancellationToken);
}
else
{
// evaluation cancelled - watch for any changes in the directory tree containing the root project:
- fileWatcher.WatchContainingDirectories([RootFileSetFactory.RootProjectFile], includeSubdirectories: true);
+ fileWatcher.WatchContainingDirectories([_fileSetFactory.RootProjectFile], includeSubdirectories: true);
_ = await fileWatcher.WaitForFileChangeAsync(
acceptChange: change => AcceptChange(change),
- startedWatching: () => Context.Reporter.Report(MessageDescriptor.WaitingForFileChangeBeforeRestarting),
+ startedWatching: () => _context.Reporter.Report(MessageDescriptor.WaitingForFileChangeBeforeRestarting),
cancellationToken);
}
}
@@ -608,7 +612,7 @@ private bool AcceptChange(ChangedPath change, EvaluationResult evaluationResult)
//
// On the other hand, changes to source files produced by source generators will be registered
// since the changes to additional file will trigger workspace update, which will trigger the source generator.
- return !evaluationResult.ItemExclusions.IsExcluded(path, kind, Context.Reporter);
+ return !evaluationResult.ItemExclusions.IsExcluded(path, kind, _context.Reporter);
}
private bool AcceptChange(ChangedPath change)
@@ -617,7 +621,7 @@ private bool AcceptChange(ChangedPath change)
if (PathUtilities.GetContainingDirectories(path).FirstOrDefault(IsHiddenDirectory) is { } containingHiddenDir)
{
- Context.Reporter.Report(MessageDescriptor.IgnoringChangeInHiddenDirectory, containingHiddenDir, kind, path);
+ _context.Reporter.Report(MessageDescriptor.IgnoringChangeInHiddenDirectory, containingHiddenDir, kind, path);
return false;
}
@@ -703,12 +707,12 @@ internal static IEnumerable NormalizePathChanges(IEnumerable changedFiles)
@@ -722,7 +726,7 @@ void Report(ChangeKind kind)
var items = changedFiles.Where(item => item.Kind == kind).ToArray();
if (items is not [])
{
- Context.Reporter.Output(GetMessage(items, kind));
+ _context.Reporter.Output(GetMessage(items, kind));
}
}
@@ -756,7 +760,7 @@ private async ValueTask EvaluateRootProjectAsync(CancellationT
{
cancellationToken.ThrowIfCancellationRequested();
- var result = await RootFileSetFactory.TryCreateAsync(requireProjectGraph: true, cancellationToken);
+ var result = await _fileSetFactory.TryCreateAsync(requireProjectGraph: true, cancellationToken);
if (result != null)
{
Debug.Assert(result.ProjectGraph != null);
@@ -764,9 +768,9 @@ private async ValueTask EvaluateRootProjectAsync(CancellationT
}
await FileWatcher.WaitForFileChangeAsync(
- RootFileSetFactory.RootProjectFile,
- Context.Reporter,
- startedWatching: () => Context.Reporter.Report(MessageDescriptor.FixBuildError),
+ _fileSetFactory.RootProjectFile,
+ _context.Reporter,
+ startedWatching: () => _context.Reporter.Report(MessageDescriptor.FixBuildError),
cancellationToken);
}
}
@@ -778,7 +782,7 @@ await FileWatcher.WaitForFileChangeAsync(
var processSpec = new ProcessSpec
{
- Executable = Context.EnvironmentOptions.MuxerPath,
+ Executable = _context.EnvironmentOptions.MuxerPath,
WorkingDirectory = Path.GetDirectoryName(projectPath)!,
OnOutput = line =>
{
@@ -791,16 +795,16 @@ await FileWatcher.WaitForFileChangeAsync(
Arguments = ["build", projectPath, "-consoleLoggerParameters:NoSummary;Verbosity=minimal", .. buildArguments]
};
- Context.Reporter.Output($"Building {projectPath} ...");
+ _context.Reporter.Output($"Building {projectPath} ...");
- var exitCode = await Context.ProcessRunner.RunAsync(processSpec, Context.Reporter, isUserApplication: false, launchResult: null, cancellationToken);
+ var exitCode = await _context.ProcessRunner.RunAsync(processSpec, _context.Reporter, isUserApplication: false, launchResult: null, cancellationToken);
return (exitCode == 0, buildOutput.ToImmutableArray(), projectPath);
}
private string GetRelativeFilePath(string path)
{
var relativePath = path;
- var workingDirectory = Context.EnvironmentOptions.WorkingDirectory;
+ var workingDirectory = _context.EnvironmentOptions.WorkingDirectory;
if (path.StartsWith(workingDirectory, StringComparison.Ordinal) && path.Length > workingDirectory.Length)
{
relativePath = path.Substring(workingDirectory.Length);
diff --git a/src/BuiltInTools/dotnet-watch/Internal/Ensure.cs b/src/BuiltInTools/dotnet-watch/Internal/Ensure.cs
deleted file mode 100644
index 21ab1478ab41..000000000000
--- a/src/BuiltInTools/dotnet-watch/Internal/Ensure.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.DotNet.Watch
-{
- internal static class Ensure
- {
- public static T NotNull(T? obj, string paramName)
- where T : class
- {
- if (obj == null)
- {
- throw new ArgumentNullException(paramName);
- }
- return obj;
- }
-
- public static string NotNullOrEmpty(string obj, string paramName)
- {
- if (string.IsNullOrEmpty(obj))
- {
- throw new ArgumentException("Value cannot be null or an empty string.", paramName);
- }
- return obj;
- }
- }
-}
diff --git a/src/BuiltInTools/dotnet-watch/Internal/MsBuildProjectFinder.cs b/src/BuiltInTools/dotnet-watch/Internal/MsBuildProjectFinder.cs
deleted file mode 100644
index eb7ba724fa5c..000000000000
--- a/src/BuiltInTools/dotnet-watch/Internal/MsBuildProjectFinder.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Globalization;
-
-namespace Microsoft.DotNet.Watch
-{
- internal class MsBuildProjectFinder
- {
- ///
- /// Finds a compatible MSBuild project.
- /// The base directory to search
- /// The filename of the project. Can be null.
- ///
- public static string FindMsBuildProject(string searchBase, string? project)
- {
- Ensure.NotNullOrEmpty(searchBase, nameof(searchBase));
-
- var projectPath = project ?? searchBase;
-
- if (!Path.IsPathRooted(projectPath))
- {
- projectPath = Path.Combine(searchBase, projectPath);
- }
-
- if (Directory.Exists(projectPath))
- {
- var projects = Directory.EnumerateFileSystemEntries(projectPath, "*.*proj", SearchOption.TopDirectoryOnly)
- .Where(f => !".xproj".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- if (projects.Count > 1)
- {
- throw new FileNotFoundException(string.Format(CultureInfo.CurrentCulture, Resources.Error_MultipleProjectsFound, projectPath));
- }
-
- if (projects.Count == 0)
- {
- throw new FileNotFoundException(string.Format(CultureInfo.CurrentCulture, Resources.Error_NoProjectsFound, projectPath));
- }
-
- return projects[0];
- }
-
- if (!File.Exists(projectPath))
- {
- throw new FileNotFoundException(string.Format(CultureInfo.CurrentCulture, Resources.Error_ProjectPath_NotFound, projectPath));
- }
-
- return projectPath;
- }
- }
-}
diff --git a/src/BuiltInTools/dotnet-watch/Internal/ReporterTraceListener.cs b/src/BuiltInTools/dotnet-watch/Internal/ReporterTraceListener.cs
deleted file mode 100644
index f67f58af6708..000000000000
--- a/src/BuiltInTools/dotnet-watch/Internal/ReporterTraceListener.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics;
-
-namespace Microsoft.DotNet.Watch;
-
-internal class ReporterTraceListener(IReporter reporter, string emoji) : TraceListener
-{
- // unused
- public override void Write(string? message)
- => WriteLine(message);
-
- public override void WriteLine(string? message)
- {
- if (message != null)
- {
- reporter.Verbose(message, emoji);
- }
- }
-}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncher.cs b/src/BuiltInTools/dotnet-watch/Process/IRuntimeProcessLauncher.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncher.cs
rename to src/BuiltInTools/dotnet-watch/Process/IRuntimeProcessLauncher.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncherFactory.cs b/src/BuiltInTools/dotnet-watch/Process/IRuntimeProcessLauncherFactory.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncherFactory.cs
rename to src/BuiltInTools/dotnet-watch/Process/IRuntimeProcessLauncherFactory.cs
diff --git a/src/BuiltInTools/dotnet-watch/LaunchSettingsProfile.cs b/src/BuiltInTools/dotnet-watch/Process/LaunchSettingsProfile.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/LaunchSettingsProfile.cs
rename to src/BuiltInTools/dotnet-watch/Process/LaunchSettingsProfile.cs
diff --git a/src/BuiltInTools/dotnet-watch/ProcessLaunchResult.cs b/src/BuiltInTools/dotnet-watch/Process/ProcessLaunchResult.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/ProcessLaunchResult.cs
rename to src/BuiltInTools/dotnet-watch/Process/ProcessLaunchResult.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/ProcessRunner.cs b/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs
similarity index 99%
rename from src/BuiltInTools/dotnet-watch/Internal/ProcessRunner.cs
rename to src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs
index 1ebebc89b789..bd0ff20a08b3 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/ProcessRunner.cs
+++ b/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs
@@ -25,8 +25,6 @@ private sealed class ProcessState
/// True if the process is a user application, false if it is a helper process (e.g. msbuild).
public async Task RunAsync(ProcessSpec processSpec, IReporter reporter, bool isUserApplication, ProcessLaunchResult? launchResult, CancellationToken processTerminationToken)
{
- Ensure.NotNull(processSpec, nameof(processSpec));
-
var state = new ProcessState();
var stopwatch = new Stopwatch();
diff --git a/src/BuiltInTools/dotnet-watch/ProcessSpec.cs b/src/BuiltInTools/dotnet-watch/Process/ProcessSpec.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/ProcessSpec.cs
rename to src/BuiltInTools/dotnet-watch/Process/ProcessSpec.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs b/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
rename to src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs b/src/BuiltInTools/dotnet-watch/Process/RunningProject.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs
rename to src/BuiltInTools/dotnet-watch/Process/RunningProject.cs
diff --git a/src/BuiltInTools/dotnet-watch/Program.cs b/src/BuiltInTools/dotnet-watch/Program.cs
index 299e622a4334..b8cf8abc79f7 100644
--- a/src/BuiltInTools/dotnet-watch/Program.cs
+++ b/src/BuiltInTools/dotnet-watch/Program.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.Runtime.Loader;
using Microsoft.Build.Locator;
@@ -76,14 +78,8 @@ public static async Task Main(string[] args)
reporter.Verbose($"Test flags: {environmentOptions.TestFlags}");
}
- string projectPath;
- try
- {
- projectPath = MsBuildProjectFinder.FindMsBuildProject(workingDirectory, options.ProjectPath);
- }
- catch (FileNotFoundException ex)
+ if (!TryFindProject(workingDirectory, options, reporter, out var projectPath))
{
- reporter.Error(ex.Message);
errorCode = 1;
return null;
}
@@ -93,6 +89,51 @@ public static async Task Main(string[] args)
return new Program(console, reporter, rootProjectOptions, options, environmentOptions);
}
+ ///
+ /// Finds a compatible MSBuild project.
+ /// The base directory to search
+ /// The filename of the project. Can be null.
+ ///
+ private static bool TryFindProject(string searchBase, CommandLineOptions options, IReporter reporter, [NotNullWhen(true)] out string? projectPath)
+ {
+ projectPath = options.ProjectPath ?? searchBase;
+
+ if (!Path.IsPathRooted(projectPath))
+ {
+ projectPath = Path.Combine(searchBase, projectPath);
+ }
+
+ if (Directory.Exists(projectPath))
+ {
+ var projects = Directory.EnumerateFileSystemEntries(projectPath, "*.*proj", SearchOption.TopDirectoryOnly)
+ .Where(f => !".xproj".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ if (projects.Count > 1)
+ {
+ reporter.Error(string.Format(CultureInfo.CurrentCulture, Resources.Error_MultipleProjectsFound, projectPath));
+ return false;
+ }
+
+ if (projects.Count == 0)
+ {
+ reporter.Error(string.Format(CultureInfo.CurrentCulture, Resources.Error_NoProjectsFound, projectPath));
+ return false;
+ }
+
+ projectPath = projects[0];
+ return true;
+ }
+
+ if (!File.Exists(projectPath))
+ {
+ reporter.Error(string.Format(CultureInfo.CurrentCulture, Resources.Error_ProjectPath_NotFound, projectPath));
+ return false;
+ }
+
+ return true;
+ }
+
// internal for testing
internal async Task RunAsync()
{
@@ -132,8 +173,23 @@ internal async Task RunAsync()
return await ListFilesAsync(processRunner, shutdownCancellationToken);
}
- var watcher = CreateWatcher(processRunner, runtimeProcessLauncherFactory: null);
- await watcher.WatchAsync(shutdownCancellationToken);
+ if (environmentOptions.IsPollingEnabled)
+ {
+ reporter.Output("Polling file watcher is enabled");
+ }
+
+ var context = CreateContext(processRunner);
+
+ if (IsHotReoadEnabled())
+ {
+ var watcher = new HotReloadDotNetWatcher(context, console, runtimeProcessLauncherFactory: null);
+ await watcher.WatchAsync(shutdownCancellationToken);
+ }
+ else
+ {
+ await DotNetWatcher.WatchAsync(context, shutdownCancellationToken);
+ }
+
return 0;
}
catch (OperationCanceledException) when (shutdownCancellationToken.IsCancellationRequested)
@@ -155,49 +211,32 @@ internal async Task RunAsync()
}
// internal for testing
- internal Watcher CreateWatcher(ProcessRunner processRunner, IRuntimeProcessLauncherFactory? runtimeProcessLauncherFactory)
- {
- if (environmentOptions.IsPollingEnabled)
+ internal DotNetWatchContext CreateContext(ProcessRunner processRunner)
+ => new()
{
- reporter.Output("Polling file watcher is enabled");
- }
-
- var fileSetFactory = new MSBuildFileSetFactory(
- rootProjectOptions.ProjectPath,
- rootProjectOptions.BuildArguments,
- environmentOptions,
- processRunner,
- reporter);
+ Reporter = reporter,
+ ProcessRunner = processRunner,
+ Options = options.GlobalOptions,
+ EnvironmentOptions = environmentOptions,
+ RootProjectOptions = rootProjectOptions,
+ };
- bool enableHotReload;
+ private bool IsHotReoadEnabled()
+ {
if (rootProjectOptions.Command != "run")
{
reporter.Verbose($"Command '{rootProjectOptions.Command}' does not support Hot Reload.");
- enableHotReload = false;
+ return false;
}
- else if (options.GlobalOptions.NoHotReload)
+
+ if (options.GlobalOptions.NoHotReload)
{
reporter.Verbose("Hot Reload disabled by command line switch.");
- enableHotReload = false;
+ return false;
}
- else
- {
- reporter.Report(MessageDescriptor.WatchingWithHotReload);
- enableHotReload = true;
- }
-
- var context = new DotNetWatchContext
- {
- Reporter = reporter,
- ProcessRunner = processRunner,
- Options = options.GlobalOptions,
- EnvironmentOptions = environmentOptions,
- RootProjectOptions = rootProjectOptions,
- };
- return enableHotReload
- ? new HotReloadDotNetWatcher(context, console, fileSetFactory, runtimeProcessLauncherFactory)
- : new DotNetWatcher(context, fileSetFactory);
+ reporter.Report(MessageDescriptor.WatchingWithHotReload);
+ return true;
}
private async Task ListFilesAsync(ProcessRunner processRunner, CancellationToken cancellationToken)
diff --git a/src/BuiltInTools/dotnet-watch/Utilities/BuildUtilities.cs b/src/BuiltInTools/dotnet-watch/UI/BuildOutput.cs
similarity index 97%
rename from src/BuiltInTools/dotnet-watch/Utilities/BuildUtilities.cs
rename to src/BuiltInTools/dotnet-watch/UI/BuildOutput.cs
index 291402fc337a..34fa4bed9964 100644
--- a/src/BuiltInTools/dotnet-watch/Utilities/BuildUtilities.cs
+++ b/src/BuiltInTools/dotnet-watch/UI/BuildOutput.cs
@@ -5,7 +5,7 @@
namespace Microsoft.DotNet.Watch;
-internal static partial class BuildUtilities
+internal static partial class BuildOutput
{
private const string BuildEmoji = "🔨";
private static readonly Regex s_buildDiagnosticRegex = GetBuildDiagnosticRegex();
diff --git a/src/BuiltInTools/dotnet-watch/Internal/ConsoleInputReader.cs b/src/BuiltInTools/dotnet-watch/UI/ConsoleInputReader.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/ConsoleInputReader.cs
rename to src/BuiltInTools/dotnet-watch/UI/ConsoleInputReader.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs b/src/BuiltInTools/dotnet-watch/UI/ConsoleReporter.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs
rename to src/BuiltInTools/dotnet-watch/UI/ConsoleReporter.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/IConsole.cs b/src/BuiltInTools/dotnet-watch/UI/IConsole.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/IConsole.cs
rename to src/BuiltInTools/dotnet-watch/UI/IConsole.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs b/src/BuiltInTools/dotnet-watch/UI/IReporter.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/IReporter.cs
rename to src/BuiltInTools/dotnet-watch/UI/IReporter.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/NullReporter.cs b/src/BuiltInTools/dotnet-watch/UI/NullReporter.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/NullReporter.cs
rename to src/BuiltInTools/dotnet-watch/UI/NullReporter.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/OutputLine.cs b/src/BuiltInTools/dotnet-watch/UI/OutputLine.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/OutputLine.cs
rename to src/BuiltInTools/dotnet-watch/UI/OutputLine.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/PhysicalConsole.cs b/src/BuiltInTools/dotnet-watch/UI/PhysicalConsole.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/PhysicalConsole.cs
rename to src/BuiltInTools/dotnet-watch/UI/PhysicalConsole.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/ProjectSpecificReporter.cs b/src/BuiltInTools/dotnet-watch/UI/ProjectSpecificReporter.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/ProjectSpecificReporter.cs
rename to src/BuiltInTools/dotnet-watch/UI/ProjectSpecificReporter.cs
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/RestartPrompt.cs b/src/BuiltInTools/dotnet-watch/UI/RestartPrompt.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/HotReload/RestartPrompt.cs
rename to src/BuiltInTools/dotnet-watch/UI/RestartPrompt.cs
diff --git a/src/BuiltInTools/dotnet-watch/Internal/CommandLineUtilities.cs b/src/BuiltInTools/dotnet-watch/Utilities/CommandLineUtilities.cs
similarity index 100%
rename from src/BuiltInTools/dotnet-watch/Internal/CommandLineUtilities.cs
rename to src/BuiltInTools/dotnet-watch/Utilities/CommandLineUtilities.cs
diff --git a/src/BuiltInTools/dotnet-watch/Filters/BuildEvaluator.cs b/src/BuiltInTools/dotnet-watch/Watch/BuildEvaluator.cs
similarity index 73%
rename from src/BuiltInTools/dotnet-watch/Filters/BuildEvaluator.cs
rename to src/BuiltInTools/dotnet-watch/Watch/BuildEvaluator.cs
index ae32359c539f..5a74aa5b032e 100644
--- a/src/BuiltInTools/dotnet-watch/Filters/BuildEvaluator.cs
+++ b/src/BuiltInTools/dotnet-watch/Watch/BuildEvaluator.cs
@@ -6,7 +6,7 @@
namespace Microsoft.DotNet.Watch
{
- internal class BuildEvaluator(DotNetWatchContext context, MSBuildFileSetFactory rootProjectFileSetFactory)
+ internal class BuildEvaluator
{
// File types that require an MSBuild re-evaluation
private static readonly string[] s_msBuildFileExtensions = new[]
@@ -18,6 +18,9 @@ internal class BuildEvaluator(DotNetWatchContext context, MSBuildFileSetFactory
.Select(e => e.GetHashCode(StringComparison.OrdinalIgnoreCase))
.ToArray();
+ private readonly MSBuildFileSetFactory _fileSetFactory;
+ private readonly DotNetWatchContext _context;
+
private List<(string fileName, DateTime lastWriteTimeUtc)>? _msbuildFileTimestamps;
// result of the last evaluation, or null if no evaluation has been performed yet.
@@ -25,29 +28,38 @@ internal class BuildEvaluator(DotNetWatchContext context, MSBuildFileSetFactory
public bool RequiresRevaluation { get; set; }
+ public BuildEvaluator(DotNetWatchContext context)
+ {
+ _context = context;
+ _fileSetFactory = CreateMSBuildFileSetFactory();
+ }
+
+ protected virtual MSBuildFileSetFactory CreateMSBuildFileSetFactory()
+ => _context.CreateMSBuildFileSetFactory();
+
public IReadOnlyList GetProcessArguments(int iteration)
{
- if (!context.EnvironmentOptions.SuppressMSBuildIncrementalism &&
+ if (!_context.EnvironmentOptions.SuppressMSBuildIncrementalism &&
iteration > 0 &&
- CommandLineOptions.IsCodeExecutionCommand(context.RootProjectOptions.Command))
+ CommandLineOptions.IsCodeExecutionCommand(_context.RootProjectOptions.Command))
{
if (RequiresRevaluation)
{
- context.Reporter.Verbose("Cannot use --no-restore since msbuild project files have changed.");
+ _context.Reporter.Verbose("Cannot use --no-restore since msbuild project files have changed.");
}
else
{
- context.Reporter.Verbose("Modifying command to use --no-restore");
- return [context.RootProjectOptions.Command, "--no-restore", .. context.RootProjectOptions.CommandArguments];
+ _context.Reporter.Verbose("Modifying command to use --no-restore");
+ return [_context.RootProjectOptions.Command, "--no-restore", .. _context.RootProjectOptions.CommandArguments];
}
}
- return [context.RootProjectOptions.Command, .. context.RootProjectOptions.CommandArguments];
+ return [_context.RootProjectOptions.Command, .. _context.RootProjectOptions.CommandArguments];
}
public async ValueTask EvaluateAsync(ChangedFile? changedFile, CancellationToken cancellationToken)
{
- if (context.EnvironmentOptions.SuppressMSBuildIncrementalism)
+ if (_context.EnvironmentOptions.SuppressMSBuildIncrementalism)
{
RequiresRevaluation = true;
return _evaluationResult = await CreateEvaluationResult(cancellationToken);
@@ -60,7 +72,7 @@ public async ValueTask EvaluateAsync(ChangedFile? changedFile,
if (RequiresRevaluation)
{
- context.Reporter.Verbose("Evaluating dotnet-watch file set.");
+ _context.Reporter.Verbose("Evaluating dotnet-watch file set.");
var result = await CreateEvaluationResult(cancellationToken);
_msbuildFileTimestamps = GetMSBuildFileTimeStamps(result);
@@ -77,16 +89,16 @@ private async ValueTask CreateEvaluationResult(CancellationTok
{
cancellationToken.ThrowIfCancellationRequested();
- var result = await rootProjectFileSetFactory.TryCreateAsync(requireProjectGraph: true, cancellationToken);
+ var result = await _fileSetFactory.TryCreateAsync(requireProjectGraph: true, cancellationToken);
if (result != null)
{
return result;
}
await FileWatcher.WaitForFileChangeAsync(
- rootProjectFileSetFactory.RootProjectFile,
- context.Reporter,
- startedWatching: () => context.Reporter.Report(MessageDescriptor.FixBuildError),
+ _fileSetFactory.RootProjectFile,
+ _context.Reporter,
+ startedWatching: () => _context.Reporter.Report(MessageDescriptor.FixBuildError),
cancellationToken);
}
}
@@ -110,7 +122,7 @@ private bool RequiresMSBuildRevaluation(FileItem? changedFile)
{
if (GetLastWriteTimeUtcSafely(file) != lastWriteTimeUtc)
{
- context.Reporter.Verbose($"Re-evaluation needed due to changes in {file}.");
+ _context.Reporter.Verbose($"Re-evaluation needed due to changes in {file}.");
return true;
}
@@ -133,7 +145,7 @@ private bool RequiresMSBuildRevaluation(FileItem? changedFile)
return msbuildFiles;
}
- private protected virtual DateTime GetLastWriteTimeUtcSafely(string file)
+ protected virtual DateTime GetLastWriteTimeUtcSafely(string file)
{
try
{
@@ -145,7 +157,7 @@ private protected virtual DateTime GetLastWriteTimeUtcSafely(string file)
}
}
- static bool IsMsBuildFileExtension(string fileName)
+ private static bool IsMsBuildFileExtension(string fileName)
{
var extension = Path.GetExtension(fileName.AsSpan());
var hashCode = string.GetHashCode(extension, StringComparison.OrdinalIgnoreCase);
diff --git a/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/Watch/DotNetWatcher.cs
similarity index 81%
rename from src/BuiltInTools/dotnet-watch/DotNetWatcher.cs
rename to src/BuiltInTools/dotnet-watch/Watch/DotNetWatcher.cs
index 36714a4c200f..b7901c81db05 100644
--- a/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs
+++ b/src/BuiltInTools/dotnet-watch/Watch/DotNetWatcher.cs
@@ -7,30 +7,30 @@
namespace Microsoft.DotNet.Watch
{
- internal sealed class DotNetWatcher(DotNetWatchContext context, MSBuildFileSetFactory fileSetFactory) : Watcher(context, fileSetFactory)
+ internal static class DotNetWatcher
{
- public override async Task WatchAsync(CancellationToken shutdownCancellationToken)
+ public static async Task WatchAsync(DotNetWatchContext context, CancellationToken shutdownCancellationToken)
{
var cancelledTaskSource = new TaskCompletionSource();
shutdownCancellationToken.Register(state => ((TaskCompletionSource)state!).TrySetResult(),
cancelledTaskSource);
- if (Context.EnvironmentOptions.SuppressMSBuildIncrementalism)
+ if (context.EnvironmentOptions.SuppressMSBuildIncrementalism)
{
- Context.Reporter.Verbose("MSBuild incremental optimizations suppressed.");
+ context.Reporter.Verbose("MSBuild incremental optimizations suppressed.");
}
var environmentBuilder = EnvironmentVariablesBuilder.FromCurrentEnvironment();
ChangedFile? changedFile = null;
- var buildEvaluator = new BuildEvaluator(Context, RootFileSetFactory);
- await using var browserConnector = new BrowserConnector(Context);
+ var buildEvaluator = new BuildEvaluator(context);
+ await using var browserConnector = new BrowserConnector(context);
for (var iteration = 0;;iteration++)
{
if (await buildEvaluator.EvaluateAsync(changedFile, shutdownCancellationToken) is not { } evaluationResult)
{
- Context.Reporter.Error("Failed to find a list of files to watch");
+ context.Reporter.Error("Failed to find a list of files to watch");
return;
}
@@ -39,20 +39,20 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
if (evaluationResult.ProjectGraph != null)
{
projectRootNode = evaluationResult.ProjectGraph.GraphRoots.Single();
- var projectMap = new ProjectNodeMap(evaluationResult.ProjectGraph, Context.Reporter);
- staticFileHandler = new StaticFileHandler(Context.Reporter, projectMap, browserConnector);
+ var projectMap = new ProjectNodeMap(evaluationResult.ProjectGraph, context.Reporter);
+ staticFileHandler = new StaticFileHandler(context.Reporter, projectMap, browserConnector);
}
else
{
- Context.Reporter.Verbose("Unable to determine if this project is a webapp.");
+ context.Reporter.Verbose("Unable to determine if this project is a webapp.");
projectRootNode = null;
staticFileHandler = null;
}
var processSpec = new ProcessSpec
{
- Executable = Context.EnvironmentOptions.MuxerPath,
- WorkingDirectory = Context.EnvironmentOptions.WorkingDirectory,
+ Executable = context.EnvironmentOptions.MuxerPath,
+ WorkingDirectory = context.EnvironmentOptions.WorkingDirectory,
Arguments = buildEvaluator.GetProcessArguments(iteration),
EnvironmentVariables =
{
@@ -62,7 +62,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
};
var browserRefreshServer = (projectRootNode != null)
- ? await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectRootNode, processSpec, environmentBuilder, Context.RootProjectOptions, DefaultAppModel.Instance, shutdownCancellationToken)
+ ? await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectRootNode, processSpec, environmentBuilder, context.RootProjectOptions, DefaultAppModel.Instance, shutdownCancellationToken)
: null;
environmentBuilder.SetProcessEnvironmentVariables(processSpec);
@@ -77,11 +77,11 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
using var currentRunCancellationSource = new CancellationTokenSource();
using var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(shutdownCancellationToken, currentRunCancellationSource.Token);
- using var fileSetWatcher = new FileWatcher(Context.Reporter);
+ using var fileSetWatcher = new FileWatcher(context.Reporter);
fileSetWatcher.WatchContainingDirectories(evaluationResult.Files.Keys, includeSubdirectories: true);
- var processTask = Context.ProcessRunner.RunAsync(processSpec, Context.Reporter, isUserApplication: true, launchResult: null, combinedCancellationSource.Token);
+ var processTask = context.ProcessRunner.RunAsync(processSpec, context.Reporter, isUserApplication: true, launchResult: null, combinedCancellationSource.Token);
Task fileSetTask;
Task finishedTask;
@@ -122,7 +122,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
// Now wait for a file to change before restarting process
changedFile = await fileSetWatcher.WaitForFileChangeAsync(
evaluationResult.Files,
- startedWatching: () => Context.Reporter.Report(MessageDescriptor.WaitingForFileChangeBeforeRestarting),
+ startedWatching: () => context.Reporter.Report(MessageDescriptor.WaitingForFileChangeBeforeRestarting),
shutdownCancellationToken);
}
else
@@ -130,7 +130,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
Debug.Assert(finishedTask == fileSetTask);
changedFile = fileSetTask.Result;
Debug.Assert(changedFile != null, "ChangedFile should only be null when cancelled");
- Context.Reporter.Output($"File changed: {changedFile.Value.Item.FilePath}");
+ context.Reporter.Output($"File changed: {changedFile.Value.Item.FilePath}");
}
}
}
diff --git a/src/BuiltInTools/dotnet-watch/Watcher.cs b/src/BuiltInTools/dotnet-watch/Watcher.cs
deleted file mode 100644
index 5a6f3240b20b..000000000000
--- a/src/BuiltInTools/dotnet-watch/Watcher.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-
-namespace Microsoft.DotNet.Watch
-{
- internal abstract class Watcher(DotNetWatchContext context, MSBuildFileSetFactory rootFileSetFactory)
- {
- public DotNetWatchContext Context => context;
- public MSBuildFileSetFactory RootFileSetFactory => rootFileSetFactory;
-
- public abstract Task WatchAsync(CancellationToken shutdownCancellationToken);
- }
-}
diff --git a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
index a9d3d3781a64..a7cdfc1169d1 100644
--- a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
+++ b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
@@ -29,7 +29,7 @@
-
+
@@ -68,17 +68,10 @@
Keep excluded files in sync with the list in GenerateLayout.targets.
-->
- <_DotnetWatchInputFile Include="$(TargetDir)**"
- Condition="('%(Filename)' != 'Microsoft.CodeAnalysis' and
- '%(Filename)' != 'Microsoft.CodeAnalysis.resources' and
- '%(Filename)' != 'Microsoft.CodeAnalysis.CSharp' and
- '%(Filename)' != 'Microsoft.CodeAnalysis.CSharp.resources') or
- $([MSBuild]::ValueOrDefault('%(FullPath)', '').Contains('BuildHost'))" />
+ <_DotnetWatchInputFile Include="$(TargetDir)**" Condition="('%(Filename)' != 'Microsoft.CodeAnalysis' and
'%(Filename)' != 'Microsoft.CodeAnalysis.resources' and
'%(Filename)' != 'Microsoft.CodeAnalysis.CSharp' and
'%(Filename)' != 'Microsoft.CodeAnalysis.CSharp.resources') or
$([MSBuild]::ValueOrDefault('%(FullPath)', '').Contains('BuildHost'))" />
-
+
diff --git a/test/Microsoft.Extensions.DotNetDeltaApplier.Tests/Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj b/test/Microsoft.Extensions.DotNetDeltaApplier.Tests/Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj
index 1d8dddb75f81..9d8892a21d53 100644
--- a/test/Microsoft.Extensions.DotNetDeltaApplier.Tests/Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj
+++ b/test/Microsoft.Extensions.DotNetDeltaApplier.Tests/Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/test/dotnet-watch.Tests/Watch/BrowserLaunchTests.cs b/test/dotnet-watch.Tests/Browser/BrowserLaunchTests.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Watch/BrowserLaunchTests.cs
rename to test/dotnet-watch.Tests/Browser/BrowserLaunchTests.cs
diff --git a/test/dotnet-watch.Tests/FileSetSerializerTests.cs b/test/dotnet-watch.Tests/Build/FileSetSerializerTests.cs
similarity index 100%
rename from test/dotnet-watch.Tests/FileSetSerializerTests.cs
rename to test/dotnet-watch.Tests/Build/FileSetSerializerTests.cs
diff --git a/test/dotnet-watch.Tests/MsBuildFileSetFactoryTest.cs b/test/dotnet-watch.Tests/Build/MsBuildFileSetFactoryTest.cs
similarity index 100%
rename from test/dotnet-watch.Tests/MsBuildFileSetFactoryTest.cs
rename to test/dotnet-watch.Tests/Build/MsBuildFileSetFactoryTest.cs
diff --git a/test/dotnet-watch.Tests/CommandLineOptionsTests.cs b/test/dotnet-watch.Tests/CommandLine/CommandLineOptionsTests.cs
similarity index 100%
rename from test/dotnet-watch.Tests/CommandLineOptionsTests.cs
rename to test/dotnet-watch.Tests/CommandLine/CommandLineOptionsTests.cs
diff --git a/test/dotnet-watch.Tests/Internal/EnvironmentVariablesBuilderTests.cs b/test/dotnet-watch.Tests/CommandLine/EnvironmentVariablesBuilderTests.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Internal/EnvironmentVariablesBuilderTests.cs
rename to test/dotnet-watch.Tests/CommandLine/EnvironmentVariablesBuilderTests.cs
diff --git a/test/dotnet-watch.Tests/Watch/DotNetWatcherTests.cs b/test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Watch/DotNetWatcherTests.cs
rename to test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs
diff --git a/test/dotnet-watch.Tests/Watch/ProgramTests.cs b/test/dotnet-watch.Tests/CommandLine/ProgramTests.cs
similarity index 95%
rename from test/dotnet-watch.Tests/Watch/ProgramTests.cs
rename to test/dotnet-watch.Tests/CommandLine/ProgramTests.cs
index f6004bb53143..8d36b33c2093 100644
--- a/test/dotnet-watch.Tests/Watch/ProgramTests.cs
+++ b/test/dotnet-watch.Tests/CommandLine/ProgramTests.cs
@@ -342,5 +342,27 @@ public async Task ProjectGraphLoadFailure()
App.AssertOutputContains(@"dotnet watch ⌚ Failed to load project graph.");
App.AssertOutputContains($"dotnet watch ❌ The project file could not be loaded. Could not find a part of the path '{Path.Combine(testAsset.Path, "AppWithDeps", "NonExistentDirectory", "X.csproj")}'");
}
+
+ [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
+ public async Task ListsFiles()
+ {
+ var testAsset = TestAssets.CopyTestAsset("WatchGlobbingApp")
+ .WithSource();
+
+ App.DotnetWatchArgs.Clear();
+ App.Start(testAsset, ["--list"]);
+ var lines = await App.Process.GetAllOutputLinesAsync(CancellationToken.None);
+ var files = lines.Where(l => !l.StartsWith("dotnet watch ⌚") && l.Trim() != "");
+
+ AssertEx.EqualFileList(
+ testAsset.Path,
+ new[]
+ {
+ "Program.cs",
+ "include/Foo.cs",
+ "WatchGlobbingApp.csproj",
+ },
+ files);
+ }
}
}
diff --git a/test/dotnet-watch.Tests/FileWatcherTests.cs b/test/dotnet-watch.Tests/FileWatcher/FileWatcherTests.cs
similarity index 100%
rename from test/dotnet-watch.Tests/FileWatcherTests.cs
rename to test/dotnet-watch.Tests/FileWatcher/FileWatcherTests.cs
diff --git a/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs b/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs
index 9a51ac843e74..db768163c52c 100644
--- a/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs
+++ b/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs
@@ -113,7 +113,7 @@ private RunningWatcher StartWatcher(TestAsset testAsset, string[] args, string w
serviceHolder.Value = s;
});
- var watcher = Assert.IsType(program.CreateWatcher(processRunner, factory));
+ var watcher = new HotReloadDotNetWatcher(program.CreateContext(processRunner), console, runtimeProcessLauncherFactory: factory);
var shutdownSource = new CancellationTokenSource();
var watchTask = Task.Run(async () =>
diff --git a/test/dotnet-watch.Tests/LaunchSettingsProfileTest.cs b/test/dotnet-watch.Tests/Process/LaunchSettingsProfileTest.cs
similarity index 100%
rename from test/dotnet-watch.Tests/LaunchSettingsProfileTest.cs
rename to test/dotnet-watch.Tests/Process/LaunchSettingsProfileTest.cs
diff --git a/test/dotnet-watch.Tests/Utilities/AssertEx.cs b/test/dotnet-watch.Tests/TestUtilities/AssertEx.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/AssertEx.cs
rename to test/dotnet-watch.Tests/TestUtilities/AssertEx.cs
diff --git a/test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs b/test/dotnet-watch.Tests/TestUtilities/AwaitableProcess.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs
rename to test/dotnet-watch.Tests/TestUtilities/AwaitableProcess.cs
diff --git a/test/dotnet-watch.Tests/Utilities/DebugTestOutputLogger.cs b/test/dotnet-watch.Tests/TestUtilities/DebugTestOutputLogger.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/DebugTestOutputLogger.cs
rename to test/dotnet-watch.Tests/TestUtilities/DebugTestOutputLogger.cs
diff --git a/test/dotnet-watch.Tests/Watch/Utilities/DotNetWatchTestBase.cs b/test/dotnet-watch.Tests/TestUtilities/DotNetWatchTestBase.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Watch/Utilities/DotNetWatchTestBase.cs
rename to test/dotnet-watch.Tests/TestUtilities/DotNetWatchTestBase.cs
diff --git a/test/dotnet-watch.Tests/Utilities/MockFileSetFactory.cs b/test/dotnet-watch.Tests/TestUtilities/MockFileSetFactory.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/MockFileSetFactory.cs
rename to test/dotnet-watch.Tests/TestUtilities/MockFileSetFactory.cs
diff --git a/test/dotnet-watch.Tests/Utilities/MockReporter.cs b/test/dotnet-watch.Tests/TestUtilities/MockReporter.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/MockReporter.cs
rename to test/dotnet-watch.Tests/TestUtilities/MockReporter.cs
diff --git a/test/dotnet-watch.Tests/Utilities/TaskExtensions.cs b/test/dotnet-watch.Tests/TestUtilities/TaskExtensions.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/TaskExtensions.cs
rename to test/dotnet-watch.Tests/TestUtilities/TaskExtensions.cs
diff --git a/test/dotnet-watch.Tests/TestUtilities/TestBuildEvaluator.cs b/test/dotnet-watch.Tests/TestUtilities/TestBuildEvaluator.cs
new file mode 100644
index 000000000000..0824f643d55c
--- /dev/null
+++ b/test/dotnet-watch.Tests/TestUtilities/TestBuildEvaluator.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.DotNet.Watch.UnitTests;
+
+internal class TestBuildEvaluator(DotNetWatchContext context, MSBuildFileSetFactory factory)
+ : BuildEvaluator(context)
+{
+ public Dictionary Timestamps { get; } = [];
+
+ protected override DateTime GetLastWriteTimeUtcSafely(string file) => Timestamps[file];
+ protected override MSBuildFileSetFactory CreateMSBuildFileSetFactory() => factory;
+}
diff --git a/test/dotnet-watch.Tests/Utilities/TestConsole.cs b/test/dotnet-watch.Tests/TestUtilities/TestConsole.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/TestConsole.cs
rename to test/dotnet-watch.Tests/TestUtilities/TestConsole.cs
diff --git a/test/dotnet-watch.Tests/Utilities/TestOptions.cs b/test/dotnet-watch.Tests/TestUtilities/TestOptions.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/TestOptions.cs
rename to test/dotnet-watch.Tests/TestUtilities/TestOptions.cs
diff --git a/test/dotnet-watch.Tests/Utilities/TestReporter.cs b/test/dotnet-watch.Tests/TestUtilities/TestReporter.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/TestReporter.cs
rename to test/dotnet-watch.Tests/TestUtilities/TestReporter.cs
diff --git a/test/dotnet-watch.Tests/Utilities/TestRuntimeProcessLauncher.cs b/test/dotnet-watch.Tests/TestUtilities/TestRuntimeProcessLauncher.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Utilities/TestRuntimeProcessLauncher.cs
rename to test/dotnet-watch.Tests/TestUtilities/TestRuntimeProcessLauncher.cs
diff --git a/test/dotnet-watch.Tests/Watch/Utilities/WatchableApp.cs b/test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs
similarity index 100%
rename from test/dotnet-watch.Tests/Watch/Utilities/WatchableApp.cs
rename to test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs
diff --git a/test/dotnet-watch.Tests/MSBuildEvaluationFilterTest.cs b/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs
similarity index 87%
rename from test/dotnet-watch.Tests/MSBuildEvaluationFilterTest.cs
rename to test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs
index 436ece5d4397..f71b5a161ad1 100644
--- a/test/dotnet-watch.Tests/MSBuildEvaluationFilterTest.cs
+++ b/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs
@@ -3,7 +3,7 @@
namespace Microsoft.DotNet.Watch.UnitTests
{
- public class MSBuildEvaluationFilterTest
+ public partial class BuildEvaluatorTests
{
private static readonly EvaluationResult s_emptyEvaluationResult = new(new Dictionary(), projectGraph: null);
@@ -30,7 +30,7 @@ public async Task ProcessAsync_EvaluatesFileSetIfProjFileChanges()
var context = CreateContext();
var fileSetFactory = new MockFileSetFactory() { TryCreateImpl = () => s_emptyEvaluationResult };
- var evaluator = new BuildEvaluator(context, fileSetFactory);
+ var evaluator = new TestBuildEvaluator(context, fileSetFactory);
await evaluator.EvaluateAsync(changedFile: null, CancellationToken.None);
@@ -48,7 +48,7 @@ public async Task ProcessAsync_DoesNotEvaluateFileSetIfNonProjFileChanges()
var counter = 0;
var fileSetFactory = new MockFileSetFactory() { TryCreateImpl = () => { counter++; return s_emptyEvaluationResult; } };
- var evaluator = new BuildEvaluator(context, fileSetFactory);
+ var evaluator = new TestBuildEvaluator(context, fileSetFactory);
await evaluator.EvaluateAsync(changedFile: null, CancellationToken.None);
@@ -68,7 +68,7 @@ public async Task ProcessAsync_EvaluateFileSetOnEveryChangeIfOptimizationIsSuppr
var counter = 0;
var fileSetFactory = new MockFileSetFactory() { TryCreateImpl = () => { counter++; return s_emptyEvaluationResult; } };
- var evaluator = new BuildEvaluator(context, fileSetFactory);
+ var evaluator = new TestBuildEvaluator(context, fileSetFactory);
await evaluator.EvaluateAsync(changedFile: null, CancellationToken.None);
@@ -98,7 +98,7 @@ public async Task ProcessAsync_SetsEvaluationRequired_IfMSBuildFileChanges_ButIs
var context = CreateContext();
- var evaluator = new TestableBuildEvaluator(context, fileSetFactory)
+ var evaluator = new TestBuildEvaluator(context, fileSetFactory)
{
Timestamps =
{
@@ -115,12 +115,5 @@ public async Task ProcessAsync_SetsEvaluationRequired_IfMSBuildFileChanges_ButIs
Assert.True(evaluator.RequiresRevaluation);
}
-
- private class TestableBuildEvaluator(DotNetWatchContext context, MSBuildFileSetFactory factory)
- : BuildEvaluator(context, factory)
- {
- public Dictionary Timestamps { get; } = [];
- private protected override DateTime GetLastWriteTimeUtcSafely(string file) => Timestamps[file];
- }
}
}
diff --git a/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs b/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs
index c42007d31818..381e1ead7f69 100644
--- a/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs
+++ b/test/dotnet-watch.Tests/Watch/GlobbingAppTests.cs
@@ -103,28 +103,6 @@ public async Task ChangeExcludedFile()
Assert.NotSame(fileChanged, finished);
}
- [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307")
- public async Task ListsFiles()
- {
- var testAsset = TestAssets.CopyTestAsset(AppName)
- .WithSource();
-
- App.DotnetWatchArgs.Clear();
- App.Start(testAsset, ["--list"]);
- var lines = await App.Process.GetAllOutputLinesAsync(CancellationToken.None);
- var files = lines.Where(l => !l.StartsWith("dotnet watch ⌚") && l.Trim() != "");
-
- AssertEx.EqualFileList(
- testAsset.Path,
- new[]
- {
- "Program.cs",
- "include/Foo.cs",
- "WatchGlobbingApp.csproj",
- },
- files);
- }
-
private async Task AssertCompiledAppDefinedTypes(int expected)
{
var prefix = "Defined types = ";
diff --git a/test/dotnet-watch.Tests/NoRestoreTests.cs b/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs
similarity index 87%
rename from test/dotnet-watch.Tests/NoRestoreTests.cs
rename to test/dotnet-watch.Tests/Watch/NoRestoreTests.cs
index b5ee7134e0c6..a837b2022fec 100644
--- a/test/dotnet-watch.Tests/NoRestoreTests.cs
+++ b/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs
@@ -27,7 +27,7 @@ private static DotNetWatchContext CreateContext(string[] args = null, Environmen
public void LeavesArgumentsUnchangedOnFirstRun()
{
var context = CreateContext();
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["run", InteractiveFlag], evaluator.GetProcessArguments(iteration: 0));
}
@@ -36,7 +36,7 @@ public void LeavesArgumentsUnchangedOnFirstRun()
public void LeavesArgumentsUnchangedIfMsBuildRevaluationIsRequired()
{
var context = CreateContext();
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["run", InteractiveFlag], evaluator.GetProcessArguments(iteration: 0));
@@ -49,7 +49,7 @@ public void LeavesArgumentsUnchangedIfMsBuildRevaluationIsRequired()
public void LeavesArgumentsUnchangedIfOptimizationIsSuppressed()
{
var context = CreateContext([], TestOptions.GetEnvironmentOptions() with { SuppressMSBuildIncrementalism = true });
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["run", InteractiveFlag], evaluator.GetProcessArguments(iteration: 0));
AssertEx.SequenceEqual(["run", InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
@@ -59,7 +59,7 @@ public void LeavesArgumentsUnchangedIfOptimizationIsSuppressed()
public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent()
{
var context = CreateContext(["--no-restore"], TestOptions.GetEnvironmentOptions() with { SuppressMSBuildIncrementalism = true });
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["run", "--no-restore", InteractiveFlag], evaluator.GetProcessArguments(iteration: 0));
AssertEx.SequenceEqual(["run", "--no-restore", InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
@@ -69,7 +69,7 @@ public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent()
public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent_UnlessAfterDashDash1()
{
var context = CreateContext(["--", "--no-restore"]);
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["run", InteractiveFlag, "--", "--no-restore"], evaluator.GetProcessArguments(iteration: 0));
AssertEx.SequenceEqual(["run", "--no-restore", InteractiveFlag, "--", "--no-restore"], evaluator.GetProcessArguments(iteration: 1));
@@ -79,7 +79,7 @@ public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent_UnlessAfterDashDas
public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent_UnlessAfterDashDash2()
{
var context = CreateContext(["--", "--", "--no-restore"]);
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["run", InteractiveFlag, "--", "--", "--no-restore"], evaluator.GetProcessArguments(iteration: 0));
AssertEx.SequenceEqual(["run", "--no-restore", InteractiveFlag, "--", "--", "--no-restore"], evaluator.GetProcessArguments(iteration: 1));
@@ -89,7 +89,7 @@ public void LeavesArgumentsUnchangedIfNoRestoreAlreadyPresent_UnlessAfterDashDas
public void AddsNoRestoreSwitch()
{
var context = CreateContext();
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["run", InteractiveFlag], evaluator.GetProcessArguments(iteration: 0));
AssertEx.SequenceEqual(["run", "--no-restore", InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
@@ -99,7 +99,7 @@ public void AddsNoRestoreSwitch()
public void AddsNoRestoreSwitch_WithAdditionalArguments()
{
var context = CreateContext(["run", "-f", ToolsetInfo.CurrentTargetFramework]);
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["run", "-f", ToolsetInfo.CurrentTargetFramework, InteractiveFlag], evaluator.GetProcessArguments(iteration: 0));
AssertEx.SequenceEqual(["run", "--no-restore", "-f", ToolsetInfo.CurrentTargetFramework, InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
@@ -109,7 +109,7 @@ public void AddsNoRestoreSwitch_WithAdditionalArguments()
public void AddsNoRestoreSwitch_ForTestCommand()
{
var context = CreateContext(["test", "--filter SomeFilter"]);
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["test", InteractiveFlag, "--filter SomeFilter"], evaluator.GetProcessArguments(iteration: 0));
AssertEx.SequenceEqual(["test", "--no-restore", InteractiveFlag, "--filter SomeFilter"], evaluator.GetProcessArguments(iteration: 1));
@@ -119,7 +119,7 @@ public void AddsNoRestoreSwitch_ForTestCommand()
public void DoesNotModifyArgumentsForUnknownCommands()
{
var context = CreateContext(["pack"]);
- var evaluator = new BuildEvaluator(context, new MockFileSetFactory());
+ var evaluator = new BuildEvaluator(context);
AssertEx.SequenceEqual(["pack", InteractiveFlag], evaluator.GetProcessArguments(iteration: 0));
AssertEx.SequenceEqual(["pack", InteractiveFlag], evaluator.GetProcessArguments(iteration: 1));
diff --git a/test/dotnet-watch.Tests/dotnet-watch.Tests.csproj b/test/dotnet-watch.Tests/dotnet-watch.Tests.csproj
index 774159200782..0bd6266b9abf 100644
--- a/test/dotnet-watch.Tests/dotnet-watch.Tests.csproj
+++ b/test/dotnet-watch.Tests/dotnet-watch.Tests.csproj
@@ -6,8 +6,8 @@
Microsoft.DotNet.Watcher.Tools
-
-
+
+