From fe71b2cc13f72d4e500393099660a32b91c138cf Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 2 Feb 2026 17:56:27 +0100 Subject: [PATCH 1/2] [msbuild] Fix listing devices with no udid. Fixes #24605. In some cases, `devicectl` may list a device with no udid, so handle this by using the 'identifier' value as the udid. Fixes this problem: ``` error MSB4028: The "GetAvailableDevices" task's outputs could not be retrieved from the "DiscardedDevices" parameter. Parameter "includeEscaped" cannot have zero length. ``` Fixes https://github.com/dotnet/macios/issues/24605. --- .../Tasks/GetAvailableDevices.cs | 14 +++++ .../TaskTests/GetAvailableDevicesTest.cs | 51 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs index 7b26fc4fb179..1c52948061fe 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs @@ -31,6 +31,9 @@ public class GetAvailableDevices : XamarinTask, ICancelableTask { CancellationTokenSource? cancellationTokenSource; + public string DeviceCtlFile { get; set; } = ""; + public string SimCtlFile { get; set; } = ""; + public override bool Execute () { if (ShouldExecuteRemotely ()) @@ -149,6 +152,10 @@ public DeviceInfo (ITaskItem item, IEnumerable runtimeIdentifiers, Apple protected virtual async System.Threading.Tasks.Task ExecuteCtlAsync (params string [] args) { + var file = args [0] == "devicectl" ? DeviceCtlFile : SimCtlFile; + if (File.Exists (file)) + return File.ReadAllText (file); + var tmpfile = Path.GetTempFileName (); try { var arguments = new List (args) { @@ -180,6 +187,7 @@ async System.Threading.Tasks.Task> RunDeviceCtlAsync () foreach (var device in array) { var name = device.GetStringPropertyOrEmpty ("deviceProperties", "name"); var udid = device.GetStringPropertyOrEmpty ("hardwareProperties", "udid"); + var identifier = device.GetStringPropertyOrEmpty ("identifier"); var deviceProperties = device.GetNullableProperty ("deviceProperties"); var buildVersion = deviceProperties.GetStringPropertyOrEmpty ("osBuildUpdate"); @@ -200,6 +208,12 @@ async System.Threading.Tasks.Task> RunDeviceCtlAsync () var transportType = connectionProperties.GetStringPropertyOrEmpty ("transportType"); var pairingState = connectionProperties.GetStringPropertyOrEmpty ("pairingState"); + if (string.IsNullOrEmpty (udid)) + udid = identifier; + + if (string.IsNullOrEmpty (udid)) + udid = $""; + var item = new TaskItem (udid); item.SetMetadata ("Name", name); item.SetMetadata ("BuildVersion", buildVersion); diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GetAvailableDevicesTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GetAvailableDevicesTest.cs index afe4da191c1a..beb8be0f356d 100644 --- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GetAvailableDevicesTest.cs +++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/GetAvailableDevicesTest.cs @@ -679,6 +679,23 @@ public void Ctl1_AppleTV () }); } + [Test] + public void DeviceCtl2_Mac () + { + var platform = ApplePlatform.iOS; + var task = CreateTask (platform, "", DEVICECTL_JSON_2); + Assert.IsTrue (task.Execute (), "Task should have succeeded."); + + Assert.Multiple (() => { + Assert.That (task.DiscardedDevices [0].ItemSpec, Is.EqualTo ("12345678-1234-1234-ABCD-1234567980AB"), "Discarded Device 1 itemspec mismatch."); + Assert.That (task.DiscardedDevices [0].GetMetadata ("Description"), Is.EqualTo (""), "Discarded Device 1 Name mismatch."); + Assert.That (task.DiscardedDevices [0].GetMetadata ("OSVersion"), Is.EqualTo (""), "Discarded Device 1 OSVersion mismatch."); + Assert.That (task.DiscardedDevices [0].GetMetadata ("UDID"), Is.EqualTo ("12345678-1234-1234-ABCD-1234567980AB"), "Discarded Device 1 UDID mismatch."); + Assert.That (task.DiscardedDevices [0].GetMetadata ("RuntimeIdentifier"), Is.EqualTo (""), "Discarded Device 1 RuntimeIdentifier mismatch."); + Assert.That (task.DiscardedDevices [0].GetMetadata ("DiscardedReason"), Is.EqualTo ("'mac' devices are not supported"), "Discarded Device 1 reason mismatch."); + }); + } + const string DEVICECTL_JSON_1 = """ { @@ -897,6 +914,40 @@ public void Ctl1_AppleTV () } """; + const string DEVICECTL_JSON_2 = + """ + { + "result" : { + "devices" : [ + { + "connectionProperties" : { + "isMobileDeviceOnly" : false, + "pairingState" : "unsupported", + "potentialHostnames" : [ + "12345678-1234-1234-ABCD-1234567980AB.coredevice.local" + ], + "tunnelState" : "unavailable" + }, + "deviceProperties" : { + "bootState" : "booted", + "ddiServicesAvailable" : false, + "providerSpecificValues" : { + "hasAMRestorableDeviceRef" : "true" + } + }, + "hardwareProperties" : { + "deviceType" : "mac", + "platform" : "macOS", + "productType" : "MacBookPro17,1" + }, + "identifier" : "12345678-1234-1234-ABCD-1234567980AB", + "visibilityClass" : "default" + } + ] + } + } + """; + const string SIMCTL_JSON_1 = """ { From f6fa34ed6fd2fad636b326ecd6b1d5ac251dcb99 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 2 Feb 2026 18:27:45 +0100 Subject: [PATCH 2/2] Remove accidental inclusion. --- msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs index 1c52948061fe..eda5380ab2d9 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/GetAvailableDevices.cs @@ -31,9 +31,6 @@ public class GetAvailableDevices : XamarinTask, ICancelableTask { CancellationTokenSource? cancellationTokenSource; - public string DeviceCtlFile { get; set; } = ""; - public string SimCtlFile { get; set; } = ""; - public override bool Execute () { if (ShouldExecuteRemotely ()) @@ -152,10 +149,6 @@ public DeviceInfo (ITaskItem item, IEnumerable runtimeIdentifiers, Apple protected virtual async System.Threading.Tasks.Task ExecuteCtlAsync (params string [] args) { - var file = args [0] == "devicectl" ? DeviceCtlFile : SimCtlFile; - if (File.Exists (file)) - return File.ReadAllText (file); - var tmpfile = Path.GetTempFileName (); try { var arguments = new List (args) {