Skip to content

Conversation

@rolfbjarne
Copy link
Member

@rolfbjarne rolfbjarne commented Nov 18, 2025

This consists of two parts:

  • Add an MSBuild target that lists all the available devices (ComputeAvailableDevices), by returning them in a $(Devices) item group.
  • Add support for the $(Device) property to specify the device or simulator to use.

Regarding the device list, we'll filter the returned list by:

  • Platform (don't return an Apple TV simulator for an iOS app).
  • Minimum OS version (not return an iOS 18.0 device when the app's minimum OS version is 26.0).
  • Only devices that are actually available, as reported by devicectl or simctl.
  • Don't return an arm64 simulator on an x64 machine.
  • RuntimeIdentifier, if specified on the command-line.

References:

Fixes #23995.

@rolfbjarne
Copy link
Member Author

@jonathanpeppers I think the spec needs to be updated so that the correct RuntimeIdentifier is set, depending on what the user chose - maybe the returned item group with the devices can have a 'RuntimeIdentifier' metadata that, if specified, is set for the subsequent build/run operation?

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 adds support for listing available devices and simulators for iOS and tvOS apps, enabling device selection via the dotnet run --device command. The implementation follows the .NET SDK's device selection specification for MAUI-style device management.

Key Changes:

  • New GetAvailableDevices MSBuild task that queries devicectl and simctl to retrieve available physical devices and simulators
  • New ComputeAvailableDevices target that can be invoked to list all compatible devices
  • Property rename from _DeviceName to Device for specifying target devices

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tools/common/JsonExtensions.cs New utility class with JSON parsing extension methods for processing devicectl/simctl output
tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GetAvailableDevicesTest.cs Comprehensive test suite covering device filtering scenarios (platform, OS version, device family)
msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs Main task implementation that queries devices, applies filters, and returns available devices
msbuild/Xamarin.Shared/Xamarin.Shared.targets Moved _AppBundleManifestPath property definition to allow access by GetAvailableDevices
msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj Added JsonExtensions.cs to the project
dotnet/targets/Xamarin.Shared.Sdk.targets Added ComputeAvailableDevices target and Device property with backward compatibility for _DeviceName
tests/common/shared-dotnet.mk Updated to use Device property instead of deprecated _DeviceName
docs/building-apps/build-targets.md Documentation for the new ComputeAvailableDevices target
docs/building-apps/build-properties.md Documentation for the new Device property

Target that queries available devices and simulators.
This target is called by 'dotnet run' to support device selection.
Returns @(Devices) items with metadata: ???????
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

The comment states "Returns @(Devices) items with metadata: ???????" which appears to be a placeholder that was not filled in. The metadata should be documented here (Description, Type, OSVersion, UDID) similar to what's in the build-targets.md documentation.

Suggested change
Returns @(Devices) items with metadata: ???????
Returns @(Devices) items with metadata: Description, Type, OSVersion, UDID

Copilot uses AI. Check for mistakes.
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

Comment on lines 2410 to 2411
<DeviceName Condition="'$(DeviceName)' == ''">$(_DeviceName)</DeviceName>
<!-- Try to keep _DeviceName working for a while yet -->
<Device Condition="'$(Device)' == ''">$(_DeviceName)</Device>
Copy link
Member

Choose a reason for hiding this comment

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

Do you need to keep $(DeviceName) around, it's public? Maybe it's not really used/documented.

Comment on lines 2577 to 2578
Condition="'$(IsMacEnabled)' == 'true'"
SessionId="$(BuildSessionId)"
Copy link
Member

Choose a reason for hiding this comment

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

That is a question... Will this work on Windows? Ideally, it could, but that could be future PR.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@rolfbjarne rolfbjarne force-pushed the dev/rolf/dotnet-run-device-selection branch from 700ce36 to 2ccb1f6 Compare January 8, 2026 14:59
@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

@vs-mobiletools-engineering-service2

This comment has been minimized.

…mulators available to run on.

This consists of two parts:

* Add an MSBuild target that lists all the available devices (`ComputeAvailableDevices`), by returning them in a `$(Devices)` item group.
* Add support for the `$(Device)` property to specify the device or simulator to use.

Regarding the device list, we'll filter the returned list by:

* Platform (don't return an Apple TV simulator for an iOS app).
* Minimum OS version (not return an iOS 18.0 device when the app's minimum OS version is 26.0).
* Only devices that are actually available, as reported by `devicectl` or `simctl`.

References:

* dotnet/android#10576
* https://github.com/dotnet/sdk/blob/2b9fc02a265c735f2132e4e3626e94962e48bdf5/documentation/specs/dotnet-run-for-maui.md

Fixes #23995.
@rolfbjarne rolfbjarne force-pushed the dev/rolf/dotnet-run-device-selection branch from 2ccb1f6 to 6823a2b Compare January 9, 2026 11:11
@rolfbjarne rolfbjarne marked this pull request as ready for review January 9, 2026 11:11
@rolfbjarne rolfbjarne requested review from emaf and mauroa as code owners January 9, 2026 11:11
@rolfbjarne rolfbjarne requested a review from Copilot January 9, 2026 11:11
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

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

[Output]
public ITaskItem [] DiscardedDevices { get; set; } = Array.Empty<ITaskItem> ();

[Output]
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The RuntimeIdentifier property has the [Output] attribute but is being used as an input parameter. Output parameters should only be set by the task, not read as input. This property should not have the [Output] attribute if it's meant to be an input parameter for filtering devices.

Suggested change
[Output]

Copilot uses AI. Check for mistakes.
}
}

Version.TryParse (productVersion, out var minimumOSVersion);
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

Potential null reference exception: Version.TryParse may leave minimumOSVersion as null if parsing fails, but it's used directly on line 271 without a null check. Consider using a default value like 'new Version (0, 0)' if parsing fails.

Suggested change
Version.TryParse (productVersion, out var minimumOSVersion);
var minimumOSVersion = new Version (0, 0);
Version.TryParse (productVersion, out minimumOSVersion);

Copilot uses AI. Check for mistakes.
Comment on lines +277 to +279
System.Threading.Tasks.Task<IEnumerable<DeviceInfo>> RunSimCtlAsync ()
{
var doc = ExecuteCtlToJsonAsync ("simctl", "list", "--json").Result;
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

Using .Result on an async task in a non-async method can cause deadlocks. The RunSimCtlAsync method should be declared as async and use 'await' instead of '.Result'. The method signature on line 277 should be 'async System.Threading.Tasks.Task<IEnumerable> RunSimCtlAsync ()' and line 279 should use 'await ExecuteCtlToJsonAsync'.

Copilot uses AI. Check for mistakes.
Target that queries available devices and simulators.
This target is called by 'dotnet run' to support device selection.
Returns @(Devices) items with metadata: ???????
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

Incomplete documentation: The comment on line 150 says "Returns @(Devices) items with metadata: ???????" which indicates the metadata documentation is incomplete. The metadata fields should be documented here as they are in the build-targets.md file (Description, Type, OSVersion, UDID, RuntimeIdentifier).

Suggested change
Returns @(Devices) items with metadata: ???????
Returns @(Devices) items with metadata: Description, Type, OSVersion, UDID, RuntimeIdentifier

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

dotnet run will only use %(RuntimeIdentifier), but I put all kinds of other metadata that Android had mainly for debugging purposes.

@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ [CI Build #6823a2b] Build passed (Build packages) ✅

Pipeline on Agent
Hash: 6823a2b4fe0d130da44a71cffeae9369512456c0 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ [PR Build #6823a2b] Build passed (Detect API changes) ✅

Pipeline on Agent
Hash: 6823a2b4fe0d130da44a71cffeae9369512456c0 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ API diff for current PR / commit

NET (empty diffs)

✅ API diff vs stable

NET (empty diffs)

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: 6823a2b4fe0d130da44a71cffeae9369512456c0 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

✅ [CI Build #6823a2b] Build passed (Build macOS tests) ✅

Pipeline on Agent
Hash: 6823a2b4fe0d130da44a71cffeae9369512456c0 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

💻 [CI Build #6823a2b] Tests on macOS X64 - Mac Sonoma (14) passed 💻

All tests on macOS X64 - Mac Sonoma (14) passed.

Pipeline on Agent
Hash: 6823a2b4fe0d130da44a71cffeae9369512456c0 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

💻 [CI Build #6823a2b] Tests on macOS arm64 - Mac Sequoia (15) passed 💻

All tests on macOS arm64 - Mac Sequoia (15) passed.

Pipeline on Agent
Hash: 6823a2b4fe0d130da44a71cffeae9369512456c0 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

❌ [CI Build #6823a2b] Tests on macOS arm64 - Mac Tahoe (26) failed ❌

Failed tests are:

  • monotouch-test

Pipeline on Agent
Hash: 6823a2b4fe0d130da44a71cffeae9369512456c0 [PR build]

@vs-mobiletools-engineering-service2
Copy link
Collaborator

🔥 [CI Build #6823a2b] Test results 🔥

Test results

❌ Tests failed on VSTS: test results

0 tests crashed, 4 tests failed, 113 tests passed.

Failures

❌ linker tests

1 tests failed, 43 tests passed.

Failed tests

  • dont link/tvOS - simulator/Debug: Crashed

Html Report (VSDrops) Download

❌ monotouch tests (macOS)

1 tests failed, 8 tests passed.

Failed tests

  • monotouch-test/macOS/Debug: Failed (Test run failed.
    Tests run: 3462 Passed: 3300 Inconclusive: 7 Failed: 1 Ignored: 161)

Html Report (VSDrops) Download

❌ msbuild tests

1 tests failed, 1 tests passed.

Failed tests

  • MSBuild tasks tests: Failed (Execution failed with exit code 1)
    • Xamarin.MacDev.Tasks.GetAvailableDevicesTests.Ctl1: System.IndexOutOfRangeException : Index was outside the bounds of the array.Multiple failures or warnings in test:
  1. Device...
    * Xamarin.MacDev.Tasks.GetAvailableDevicesTests.Ctl1_AppleTV: System.IndexOutOfRangeException : Index was outside the bounds of the array.Multiple failures or warnings in test:
  2. Device...
    * Xamarin.MacDev.Tasks.GetAvailableDevicesTests.Ctl1_iPad: System.IndexOutOfRangeException : Index was outside the bounds of the array.Multiple failures or warnings in test:
  3. Device...
    * ... and 4 more

Html Report (VSDrops) Download

❌ xcframework tests

1 tests failed, 3 tests passed.

Failed tests

  • xcframework-test/tvOS - simulator/Debug: Crashed

Html Report (VSDrops) Download

Successes

✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (iOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (MacCatalyst): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (macOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (Multiple platforms): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (tvOS): All 1 tests passed. Html Report (VSDrops) Download
✅ framework: All 2 tests passed. Html Report (VSDrops) Download
✅ fsharp: All 4 tests passed. Html Report (VSDrops) Download
✅ generator: All 5 tests passed. Html Report (VSDrops) Download
✅ interdependent-binding-projects: All 4 tests passed. Html Report (VSDrops) Download
✅ introspection: All 4 tests passed. Html Report (VSDrops) Download
✅ monotouch (iOS): All 9 tests passed. Html Report (VSDrops) Download
✅ monotouch (MacCatalyst): All 11 tests passed. Html Report (VSDrops) Download
✅ monotouch (tvOS): All 9 tests passed. Html Report (VSDrops) Download
✅ windows: All 3 tests passed. Html Report (VSDrops) Download
✅ xtro: All 1 tests passed. Html Report (VSDrops) Download

Pipeline on Agent
Hash: 6823a2b4fe0d130da44a71cffeae9369512456c0 [PR build]

Copy link
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

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

Overall looks good! There is a possible exception the JSON parsing can throw, but might not happen in real life.

Target that queries available devices and simulators.
This target is called by 'dotnet run' to support device selection.
Returns @(Devices) items with metadata: ???????
Copy link
Member

Choose a reason for hiding this comment

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

dotnet run will only use %(RuntimeIdentifier), but I put all kinds of other metadata that Android had mainly for debugging purposes.

Comment on lines +159 to +160
await ExecuteAsync (Log, "xcrun", arguments, sdkDevPath: SdkDevPath, cancellationToken: cancellationTokenSource!.Token);
return File.ReadAllText (tmpfile);
Copy link
Member

Choose a reason for hiding this comment

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

If the file is long, you could avoid making a large string if JsonDocument opened a Stream instead.

Do you know how long the task takes? If it's fast, probably doesn't matter.


public static string? GetStringProperty (this JsonElement? element, string propertyName)
{
return GetNullableProperty (element, propertyName)?.GetString ();
Copy link
Member

Choose a reason for hiding this comment

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

The only thing I've run into is if you got values like JsonValueKind.True, JsonValueKind.Number, JsonValueKind.Null, I think it will throw InvalidOperationException:

https://github.com/dotnet/runtime/blob/881c160a342b1f20946451e264eaad7a337659da/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs#L369-L371

I had to do stuff like this in the past:

	static string? GetJsonValueAsString (JsonElement element) =>
		element.ValueKind switch {
			JsonValueKind.String => element.GetString (),
			JsonValueKind.True => "true",
			JsonValueKind.False => "false",
			JsonValueKind.Number or JsonValueKind.Object or JsonValueKind.Array => element.GetRawText (),
			_ => null, // Null or Undefined
		};

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.

Improve device selection when using 'dotnet run'

5 participants