Skip to content

Conversation

@jonathanpeppers
Copy link
Member

Context: https://github.com/dotnet/sdk/blob/5398e10de90dc9a27e0290ad55c2ae67360ea8be/documentation/specs/dotnet-run-for-maui.md

Spec Changes

Added RuntimeIdentifier Support

  • Examples to include %(RuntimeIdentifier) metadata (e.g., android-arm64, ios-arm64, iossimulator-arm64)

  • When a device provides a %(RuntimeIdentifier), it will be passed as -p:RuntimeIdentifier to subsequent MSBuild steps (build, deploy, ComputeRunArguments, run)

  • %(RuntimeIdentifier) is optional but recommended

Added Binary Logs Documentation

  • Added new section "Binary Logs for Device Selection" explaining:
    • When binlog files are created (when using -bl: with dotnet run)
    • The naming pattern: <base-name>-dotnet-run-devices.binlog

Implementation

Renamed TargetFrameworkSelector to RunCommandSelector

  • Expanded scope from just framework selection to handle both target framework and device selection

  • Made it a non-static class implementing IDisposable to:

    • Cache the MSBuild project instance across operations
    • Avoid loading/evaluating the project multiple times, except when global properties change
    • Properly manage MSBuild resources (ProjectCollection, Project, ProjectInstance) with IDisposable
  • Added InvalidateGlobalProperties() method to re-evaluate the project when needed with a $(TargetFramework) global property change.

  • Binary logger is owned by the selector instance and properly disposed.

Added Tests

  • Mock test project (DotnetRunDevices.csproj) implements ComputeAvailableDevices target that returns hardcoded device items based on the target framework

  • Test project includes GenerateDeviceInfo target that runs during build when a device is selected:

    • Generates DeviceInfo.cs with constants for $(Device) and $(RuntimeIdentifier) properties
    • Writes to intermediate output directory before compilation
  • Test application prints these generated constants, allowing tests to verify that:

    • The correct device ID was passed to MSBuild
    • $(RuntimeIdentifier) was propagated correctly (when provided by device)
    • Multi-targeted apps can have different devices per framework

Context: https://github.com/dotnet/sdk/blob/5398e10de90dc9a27e0290ad55c2ae67360ea8be/documentation/specs/dotnet-run-for-maui.md

~~ Spec Changes ~~

**Added RuntimeIdentifier Support**

- Examples to include `%(RuntimeIdentifier)` metadata
  (e.g., `android-arm64`, `ios-arm64`, `iossimulator-arm64`)

- When a device provides a `%(RuntimeIdentifier)`, it will be passed
  as `-p:RuntimeIdentifier` to subsequent MSBuild steps (build,
  deploy, ComputeRunArguments, run)

- `%(RuntimeIdentifier)` is optional but recommended

**Added Binary Logs Documentation**

- Added new section "Binary Logs for Device Selection" explaining:
  - When binlog files are created (when using `-bl:` with `dotnet run`)
  - The naming pattern: `<base-name>-dotnet-run-devices.binlog`

~~ Implementation ~~

**Renamed `TargetFrameworkSelector` to `RunCommandSelector`**

- Expanded scope from just framework selection to handle both target
  framework and device selection

- Made it a non-static class implementing `IDisposable` to:
  - Cache the MSBuild project instance across operations
  - Avoid loading/evaluating the project multiple times, except when
    global properties change
  - Properly manage MSBuild resources (ProjectCollection, Project,
    ProjectInstance) with `IDisposable`

- Added `InvalidateGlobalProperties()` method to re-evaluate the
  project when needed with a `$(TargetFramework)` global property change.

- Binary logger is owned by the `selector` instance and properly
  disposed.

**Added Tests**

- Mock test project (`DotnetRunDevices.csproj`) implements
  `ComputeAvailableDevices` target that returns hardcoded device items
  based on the target framework

- Test project includes `GenerateDeviceInfo` target that runs during
  build when a device is selected:
  - Generates `DeviceInfo.cs` with constants for `$(Device)` and
    `$(RuntimeIdentifier)` properties
  - Writes to intermediate output directory before compilation

- Test application prints these generated constants, allowing tests to
  verify that:
  - The correct device ID was passed to MSBuild
  - `$(RuntimeIdentifier)` was propagated correctly (when provided by device)
  - Multi-targeted apps can have different devices per framework
@jonathanpeppers jonathanpeppers force-pushed the dev/peppers/dotnet-run-devices branch from 5048a76 to 43482d4 Compare November 25, 2025 21:46
@jonathanpeppers jonathanpeppers marked this pull request as ready for review November 25, 2025 23:30
Copilot finished reviewing on behalf of jonathanpeppers November 25, 2025 23:33
Copy link
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

This PR implements device selection logic for dotnet run, enabling MAUI and other workloads to target specific devices (emulators, simulators, physical devices). The implementation renames TargetFrameworkSelector to RunCommandSelector and expands its scope to handle both target framework and device selection with efficient project instance caching.

Key changes:

  • Added --device and --list-devices command-line options to dotnet run
  • Implemented RunCommandSelector class with device enumeration via MSBuild's ComputeAvailableDevices target
  • Added RuntimeIdentifier propagation from device metadata to subsequent build steps
  • Comprehensive test coverage including multi-framework device selection scenarios

Reviewed changes

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

Show a summary per file
File Description
src/Cli/dotnet/Commands/Run/RunCommandSelector.cs New class handling both framework and device selection with project instance caching and IDisposable pattern
src/Cli/dotnet/Commands/Run/RunCommand.cs Integrated device selection logic into run command workflow with proper binlog support
src/Cli/dotnet/Commands/Run/RunCommandParser.cs Added DeviceOption and ListDevicesOption to command parser
src/Cli/dotnet/Commands/CliCommandStrings.resx Added localization strings for device-related messages and prompts
src/Cli/dotnet/Commands/xlf/*.xlf Updated localization files with new device-related string entries (state="new")
src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs Added ComputeAvailableDevices MSBuild target constant
test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsDevice.cs Comprehensive integration tests covering device selection scenarios
test/TestAssets/TestProjects/DotnetRunDevices/* Test project with mock device enumeration and code generation
documentation/specs/dotnet-run-for-maui.md Updated spec with RuntimeIdentifier metadata and binlog documentation
test/dotnet.Tests/CompletionTests/snapshots/* Updated shell completion snapshots for new device options

jonathanpeppers and others added 3 commits November 25, 2025 19:34
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
net8.0 target framework was crashing due to lack of Rosetta x64 -> arm64 translation.

I think we can just use net9.0 + "current" instead
Seeing if this fixes macOS arm64
@jonathanpeppers
Copy link
Member Author

After some manual editing workloads, end-to-end is working:

image

Left emulator, right is device w/ Vysor:

image

I need to do some cleanup in the Android workload to get the casing to match. adb -s ... emu avd name seems to just print lowercase.

jonathanpeppers added a commit to dotnet/android that referenced this pull request Dec 2, 2025
Context: dotnet/sdk#51337
Context: dotnet/sdk#51914

In 63f7cba, we added the `ComputeAvailableDevices` MSBuild target,
which I was able to test end-to-end:

    D:\src\helloandroid> D:\src\dotnet\sdk\artifacts\bin\redist\Debug\dotnet\dotnet.exe run -bl
    Select a device to run on:

    > 0A041FDD400327 - Pixel 5
    emulator-5554 - pixel 7 - api 36

    Type to search

Unfortunately, the AVD name is returned from `adb emu avd name`, which
simply returns the property:

    > adb -s emulator-5554 shell getprop | grep avd_name
    [ro.boot.qemu.avd_name]: [pixel_7_-_api_36]

We can call `TextInfo.ToTitleCase()`, replace underscores with spaces,
and replace "Api" with "API" to make the AVD name more user-friendly.

The only other alternative I considered was parsing the
`~/.android/avd/<name>.ini` file to get the `displayname` property,
but it would still require calling `adb emu avd name` to *get* the
path to this `.ini` file. It felt more straightforward to just format
the AVD name directly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants