Skip to content

[dotnet-watch] Add device selection support#53631

Open
jonathanpeppers wants to merge 9 commits intorelease/10.0.3xxfrom
dev/peppers/dotnet-watch-device-selection
Open

[dotnet-watch] Add device selection support#53631
jonathanpeppers wants to merge 9 commits intorelease/10.0.3xxfrom
dev/peppers/dotnet-watch-device-selection

Conversation

@jonathanpeppers
Copy link
Copy Markdown
Member

Adds device selection to dotnet-watch for MAUI/mobile scenarios, mirroring the dotnet-run device selection flow from the spec (documentation/specs/dotnet-run-for-maui.md).

Merged TargetFrameworkSelectionPrompt and the new device prompt into a single WatchSelectionPrompt (library) / SpectreWatchSelectionPrompt (console app), keeping Spectre.Console isolated to the console app project.

After TFM selection, HotReloadDotNetWatcher calls the ComputeAvailableDevices MSBuild target via in-process MSBuild. A single device is auto-selected; multiple devices show an interactive Spectre prompt with search. The selected device and its RuntimeIdentifier are passed to dotnet build (-p:Device, -p:RuntimeIdentifier) and to the launched dotnet run subprocess (--device). A re-restore is performed when the device provides a RuntimeIdentifier not present in the original restore.

Adds --device CLI option to dotnet-watch for pre-specifying a device.

Tests:

  • Unit tests for prompt selection, search, caching, and FormatDevice
  • E2E tests using DotnetRunDevices test asset: interactive device selection, single-device auto-select

Adds device selection to dotnet-watch for MAUI/mobile scenarios,
mirroring the dotnet-run device selection flow from the spec
(documentation/specs/dotnet-run-for-maui.md).

Merged TargetFrameworkSelectionPrompt and the new device prompt into a
single WatchSelectionPrompt (library) / SpectreWatchSelectionPrompt
(console app), keeping Spectre.Console isolated to the console app
project and avoiding a second constructor parameter on
HotReloadDotNetWatcher.

After TFM selection, HotReloadDotNetWatcher calls the
ComputeAvailableDevices MSBuild target via in-process MSBuild. A single
device is auto-selected; multiple devices show an interactive Spectre
prompt with search. The selected device and its RuntimeIdentifier are
passed to dotnet build (-p:Device, -p:RuntimeIdentifier) and to the
launched dotnet run subprocess (--device). A re-restore is performed
when the device provides a RuntimeIdentifier not present in the
original restore.

Adds --device CLI option to dotnet-watch for pre-specifying a device.

Tests:
- Unit tests for prompt selection, search, caching, and FormatDevice
- E2e tests using DotnetRunDevices test asset: interactive device
  selection, single-device auto-select

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jonathanpeppers jonathanpeppers requested review from a team and tmat as code owners March 30, 2026 19:05
Copilot AI review requested due to automatic review settings March 30, 2026 19:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds MAUI/mobile-oriented device selection to dotnet-watch Hot Reload, aligning the watch experience with the dotnet run device-selection spec by computing devices via MSBuild and prompting (when needed) through a Spectre.Console-based UI.

Changes:

  • Introduces a unified selection prompt abstraction (WatchSelectionPrompt) for both target framework and device selection, with a Spectre.Console implementation for interactive console scenarios.
  • Extends HotReloadDotNetWatcher to invoke ComputeAvailableDevices, auto-select a single device, prompt when multiple devices are available, and flow device/RID into build + run.
  • Adds --device to dotnet-watch, updates resources/localization, and adds unit + E2E coverage for device selection prompting/formatting.

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
test/dotnet-watch.Tests/TestUtilities/NoOpWatchSelectionPrompt.cs Adds a non-interactive prompt implementation for tests.
test/dotnet-watch.Tests/TestUtilities/DotNetWatchTestBase.cs Updates watcher construction to use WatchSelectionPrompt.
test/dotnet-watch.Tests/HotReload/WatchSelectionPromptTests.cs New unit tests covering framework/device selection, search, caching, and device formatting.
test/dotnet-watch.Tests/HotReload/TargetFrameworkSelectionPromptTests.cs Removes old prompt-specific tests after prompt unification.
test/dotnet-watch.Tests/HotReload/MauiHotReloadTests.cs Adds E2E tests for interactive device selection and single-device auto-select.
test/dotnet-watch.Tests/HotReload/BuildProjectsTests.cs Updates tests for new watcher signature and new deviceSelector parameter.
src/Dotnet.Watch/Watch/UI/WatchSelectionPrompt.cs New shared abstraction for framework + device selection with cached selections.
src/Dotnet.Watch/Watch/UI/TargetFrameworkSelectionPrompt.cs Removes superseded framework-only prompt abstraction.
src/Dotnet.Watch/Watch/UI/IReporter.cs Adds a new message descriptor related to device availability.
src/Dotnet.Watch/Watch/UI/DeviceInfo.cs Adds a record representing device metadata coming from MSBuild.
src/Dotnet.Watch/Watch/Process/ProjectLauncher.cs Passes selected device/RID into the launched dotnet run invocation.
src/Dotnet.Watch/Watch/HotReload/HotReloadDotNetWatcher.cs Implements MSBuild-driven device discovery/selection and flows results into build pipeline.
src/Dotnet.Watch/Watch/Context/ProjectOptions.cs Adds Device and DeviceRuntimeIdentifier to project launch/build options.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.zh-Hant.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.zh-Hans.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.tr.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.ru.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.pt-BR.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.pl.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.ko.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.ja.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.it.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.fr.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.es.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.de.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/xlf/Resources.cs.xlf Localization updates for new device-related strings.
src/Dotnet.Watch/dotnet-watch/Watch/BuildEvaluator.cs Ensures --device is included in the evaluated run arguments.
src/Dotnet.Watch/dotnet-watch/UI/SpectreWatchSelectionPrompt.cs Implements interactive Spectre prompt for framework + device selection.
src/Dotnet.Watch/dotnet-watch/Resources.resx Adds new device prompt/help strings.
src/Dotnet.Watch/dotnet-watch/Program.cs Switches to the unified selection prompt for Hot Reload runs.
src/Dotnet.Watch/dotnet-watch/CommandLine/DotnetWatchCommandDefinition.cs Adds the --device root option to dotnet-watch.
src/Dotnet.Watch/dotnet-watch/CommandLine/CommandLineOptions.cs Parses --device into CommandLineOptions and flows into ProjectOptions.

Comment on lines +994 to 999
var needsFrameworkSelection = targetFramework == null && frameworkSelector != null;
var needsDeviceSelection = mainProjectOptions?.Device == null && deviceSelector != null;

if (mainProjectOptions == null ||
frameworkSelector == null ||
targetFramework != null ||
(!needsFrameworkSelection && !needsDeviceSelection) ||
!mainProjectOptions.Representation.IsProjectFile)
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When --device is specified on the command line, mainProjectOptions.Device is non-null so device computation/selection is skipped and selectedDevice remains null. As a result, the subsequent build/restore invocations won’t get -p:Device=... (and no RID discovery/re-restore can happen), which breaks the intended “pre-specified device” flow. Consider treating an explicitly provided device as a pre-selection: run ComputeAvailableDevices to validate/resolve the matching device (including RuntimeIdentifier), set selectedDevice, and pass it into the build/restore path (including the re-restore when the RID is new).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is one thing we don't do for dotnet run either. If someone passes --device, we just use it and don't compute the RuntimeIdentifier.

I think it is OK for now; we haven't found a place you would need this -- you can pass -r.

- Fix Aspire build: update HotReloadDotNetWatcher constructor call
- Make WatchSelectionPrompt nullable; Aspire and tests pass null
- Remove NoPromptWatchSelectionPrompt and NoOpWatchSelectionPrompt
- Zero devices now propagates failure instead of silently continuing
- Fix stale XML doc on TrySelectDeviceAsync

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jonathanpeppers and others added 5 commits March 31, 2026 16:00
- Rename WatchSelectionPrompt to BuildParametersSelectionPrompt
- Use needsFrameworkSelection/needsDeviceSelection variables in
  selection blocks
- Use LoadedProjectGraph.TryGetProjectNode + DeepCopy instead of
  creating a new ProjectInstance
- Use ProjectBuildManager for building ComputeAvailableDevices target
- Move ComputeAvailableDevices constant to TargetNames

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mirror the targetFramework pattern: selectedDevice and
selectedDeviceRuntimeIdentifier are now string? variables initialized
from CLI options, making needsDeviceSelection consistent with
needsFrameworkSelection. BuildProjectsResult carries string? fields
instead of DeviceInfo?.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the proper CLI option for passing RuntimeIdentifier to build,
restore, and run subprocesses.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jonathanpeppers jonathanpeppers requested a review from tmat March 31, 2026 21:31
Inline the null checks from needsFrameworkSelection/needsDeviceSelection
directly into the if conditions so the compiler can see the narrowing
without needing is not null guards or ! operators.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants