diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAvailableAndroidDevices.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAvailableAndroidDevices.cs index a8b1e46f4fe..76b5545d4bd 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAvailableAndroidDevices.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAvailableAndroidDevices.cs @@ -22,200 +22,200 @@ namespace Xamarin.Android.Tasks; /// public class GetAvailableAndroidDevices : AndroidAdb { - readonly List output = []; - - /// - /// Path to the emulator tool directory. - /// - public string EmulatorToolPath { get; set; } = ""; - - /// - /// Filename of the emulator executable (e.g., "emulator" or "emulator.exe"). - /// - public string EmulatorToolExe { get; set; } = ""; - - [Output] - public ITaskItem [] Devices { get; set; } = []; - - public GetAvailableAndroidDevices () - { - Command = "devices"; - Arguments = "-l"; - } - - protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance) - { - base.LogEventsFromTextOutput (singleLine, messageImportance); - output.Add (singleLine); - } - - protected override void LogToolCommand (string message) => Log.LogDebugMessage (message); - - public override bool RunTask () - { - if (!base.RunTask ()) - return false; - - // Parse devices from adb using shared AdbRunner logic - var adbDevices = AdbRunner.ParseAdbDevicesOutput (output); - Log.LogDebugMessage ($"Found {adbDevices.Count} device(s) from adb"); - - // For emulators, query AVD names - var logger = this.CreateTaskLogger (); - foreach (var device in adbDevices) { - if (device.Type == AdbDeviceType.Emulator) { - device.AvdName = GetEmulatorAvdName (device.Serial); - device.Description = AdbRunner.BuildDeviceDescription (device, logger); - } - } - - // Get available emulators from 'emulator -list-avds' - var availableEmulators = GetAvailableEmulators (); - Log.LogDebugMessage ($"Found {availableEmulators.Count} available emulator(s) from 'emulator -list-avds'"); - - // Merge using shared logic - var mergedDevices = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators, logger); - - // Filter: if any online devices exist, return only those so auto-selection works - // when a single device is running. If none are online, return all (including - // non-running emulators) so the user can pick one to boot. - var filteredDevices = FilterDevicesForSelection (mergedDevices); - Log.LogDebugMessage ($"Filtered from {mergedDevices.Count} to {filteredDevices.Count} device(s) (online devices take priority)"); - - // Convert to ITaskItem array - Devices = ConvertToTaskItems (filteredDevices); - - Log.LogDebugMessage ($"Total {Devices.Length} Android device(s)/emulator(s) after filtering"); - - return !Log.HasLoggedErrors; - } - - /// - /// Filters the merged device list for device selection: - /// - If any online devices exist, returns only those (so auto-selection works with a single running device) - /// - If no online devices exist, returns all (including non-running emulators for user selection) - /// - internal static IReadOnlyList FilterDevicesForSelection (IReadOnlyList devices) - { - var onlineDevices = new List (devices.Count); - foreach (var device in devices) { - if (device.Status == AdbDeviceStatus.Online) { - onlineDevices.Add (device); - } - } - - if (onlineDevices.Count == 0) - return devices; - - return onlineDevices; - } - - /// - /// Converts AdbDeviceInfo list to ITaskItem array for MSBuild output. - /// - internal static ITaskItem [] ConvertToTaskItems (IReadOnlyList devices) - { - var items = new ITaskItem [devices.Count]; - for (int i = 0; i < devices.Count; i++) { - var device = devices [i]; - var item = new TaskItem (device.Serial); - item.SetMetadata ("Description", device.Description); - item.SetMetadata ("Type", device.Type.ToString ()); - item.SetMetadata ("Status", device.Status.ToString ()); - - if (!device.AvdName.IsNullOrEmpty ()) - item.SetMetadata ("AvdName", device.AvdName); - if (!device.Model.IsNullOrEmpty ()) - item.SetMetadata ("Model", device.Model); - if (!device.Product.IsNullOrEmpty ()) - item.SetMetadata ("Product", device.Product); - if (!device.Device.IsNullOrEmpty ()) - item.SetMetadata ("Device", device.Device); - if (!device.TransportId.IsNullOrEmpty ()) - item.SetMetadata ("TransportId", device.TransportId); - - items [i] = item; - } - return items; - } - - /// - /// Gets the list of available AVDs using 'emulator -list-avds'. - /// - protected virtual List GetAvailableEmulators () - { - var emulators = new List (); - - if (EmulatorToolPath.IsNullOrEmpty () || EmulatorToolExe.IsNullOrEmpty ()) { - Log.LogDebugMessage ("EmulatorToolPath or EmulatorToolExe not set, skipping emulator listing"); - return emulators; - } - - var emulatorPath = Path.Combine (EmulatorToolPath, EmulatorToolExe); - if (!File.Exists (emulatorPath)) { - Log.LogDebugMessage ($"Emulator tool not found at: {emulatorPath}"); - return emulators; - } - - try { - var exitCode = MonoAndroidHelper.RunProcess ( - emulatorPath, - "-list-avds", - Log, - onOutput: (sender, e) => { - if (!e.Data.IsNullOrWhiteSpace ()) { - var avdName = e.Data.Trim (); - emulators.Add (avdName); - Log.LogDebugMessage ($"Found available emulator: {avdName}"); - } - }, - logWarningOnFailure: false - ); - - if (exitCode != 0) { - Log.LogDebugMessage ($"'emulator -list-avds' returned exit code: {exitCode}"); - } - } catch (Exception ex) { - Log.LogDebugMessage ($"Failed to run 'emulator -list-avds': {ex.Message}"); - } - - return emulators; - } - - /// - /// Queries the emulator for its AVD name using 'adb -s <serial> emu avd name'. - /// Returns the raw AVD name (not formatted). - /// - protected virtual string? GetEmulatorAvdName (string serial) - { - try { - var adbPath = Path.Combine (ToolPath, ToolExe); - var outputLines = new List (); - - var exitCode = MonoAndroidHelper.RunProcess ( - adbPath, - $"-s {serial} emu avd name", - Log, - onOutput: (sender, e) => { - if (!e.Data.IsNullOrEmpty ()) { - outputLines.Add (e.Data); - } - }, - logWarningOnFailure: false - ); - - if (exitCode == 0 && outputLines.Count > 0) { - var avdName = outputLines [0].Trim (); - // Verify it's not the "OK" response - if (!avdName.IsNullOrEmpty () && !avdName.Equals ("OK", StringComparison.OrdinalIgnoreCase)) { - Log.LogDebugMessage ($"Emulator {serial} has AVD name: {avdName}"); - return avdName; - } - } - } catch (Exception ex) { - Log.LogDebugMessage ($"Failed to get AVD name for {serial}: {ex.Message}"); - } - - return null; - } + readonly List output = []; + + /// + /// Path to the emulator tool directory. + /// + public string EmulatorToolPath { get; set; } = ""; + + /// + /// Filename of the emulator executable (e.g., "emulator" or "emulator.exe"). + /// + public string EmulatorToolExe { get; set; } = ""; + + [Output] + public ITaskItem [] Devices { get; set; } = []; + + public GetAvailableAndroidDevices () + { + Command = "devices"; + Arguments = "-l"; + } + + protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance) + { + base.LogEventsFromTextOutput (singleLine, messageImportance); + output.Add (singleLine); + } + + protected override void LogToolCommand (string message) => Log.LogDebugMessage (message); + + public override bool RunTask () + { + if (!base.RunTask ()) + return false; + + // Parse devices from adb using shared AdbRunner logic + var adbDevices = AdbRunner.ParseAdbDevicesOutput (output); + Log.LogDebugMessage ($"Found {adbDevices.Count} device(s) from adb"); + + // For emulators, query AVD names + var logger = this.CreateTaskLogger (); + foreach (var device in adbDevices) { + if (device.Type == AdbDeviceType.Emulator) { + device.AvdName = GetEmulatorAvdName (device.Serial); + device.Description = AdbRunner.BuildDeviceDescription (device, logger); + } + } + + // Get available emulators from 'emulator -list-avds' + var availableEmulators = GetAvailableEmulators (); + Log.LogDebugMessage ($"Found {availableEmulators.Count} available emulator(s) from 'emulator -list-avds'"); + + // Merge using shared logic + var mergedDevices = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators, logger); + + // Filter: if any online devices exist, return only those so auto-selection works + // when a single device is running. If none are online, return all (including + // non-running emulators) so the user can pick one to boot. + var filteredDevices = FilterDevicesForSelection (mergedDevices); + Log.LogDebugMessage ($"Filtered from {mergedDevices.Count} to {filteredDevices.Count} device(s) (online devices take priority)"); + + // Convert to ITaskItem array + Devices = ConvertToTaskItems (filteredDevices); + + Log.LogDebugMessage ($"Total {Devices.Length} Android device(s)/emulator(s) after filtering"); + + return !Log.HasLoggedErrors; + } + + /// + /// Filters the merged device list for device selection: + /// - If any online devices exist, returns only those (so auto-selection works with a single running device) + /// - If no online devices exist, returns all (including non-running emulators for user selection) + /// + internal static IReadOnlyList FilterDevicesForSelection (IReadOnlyList devices) + { + var onlineDevices = new List (devices.Count); + foreach (var device in devices) { + if (device.Status == AdbDeviceStatus.Online) { + onlineDevices.Add (device); + } + } + + if (onlineDevices.Count == 0) + return devices; + + return onlineDevices; + } + + /// + /// Converts AdbDeviceInfo list to ITaskItem array for MSBuild output. + /// + internal static ITaskItem [] ConvertToTaskItems (IReadOnlyList devices) + { + var items = new ITaskItem [devices.Count]; + for (int i = 0; i < devices.Count; i++) { + var device = devices [i]; + var item = new TaskItem (device.Serial); + item.SetMetadata ("Description", device.Description); + item.SetMetadata ("Type", device.Type.ToString ()); + item.SetMetadata ("Status", device.Status.ToString ()); + + if (!device.AvdName.IsNullOrEmpty ()) + item.SetMetadata ("AvdName", device.AvdName); + if (!device.Model.IsNullOrEmpty ()) + item.SetMetadata ("Model", device.Model); + if (!device.Product.IsNullOrEmpty ()) + item.SetMetadata ("Product", device.Product); + if (!device.Device.IsNullOrEmpty ()) + item.SetMetadata ("Device", device.Device); + if (!device.TransportId.IsNullOrEmpty ()) + item.SetMetadata ("TransportId", device.TransportId); + + items [i] = item; + } + return items; + } + + /// + /// Gets the list of available AVDs using 'emulator -list-avds'. + /// + protected virtual List GetAvailableEmulators () + { + var emulators = new List (); + + if (EmulatorToolPath.IsNullOrEmpty () || EmulatorToolExe.IsNullOrEmpty ()) { + Log.LogDebugMessage ("EmulatorToolPath or EmulatorToolExe not set, skipping emulator listing"); + return emulators; + } + + var emulatorPath = Path.Combine (EmulatorToolPath, EmulatorToolExe); + if (!File.Exists (emulatorPath)) { + Log.LogDebugMessage ($"Emulator tool not found at: {emulatorPath}"); + return emulators; + } + + try { + var exitCode = MonoAndroidHelper.RunProcess ( + emulatorPath, + "-list-avds", + Log, + onOutput: (sender, e) => { + if (!e.Data.IsNullOrWhiteSpace ()) { + var avdName = e.Data.Trim (); + emulators.Add (avdName); + Log.LogDebugMessage ($"Found available emulator: {avdName}"); + } + }, + logWarningOnFailure: false + ); + + if (exitCode != 0) { + Log.LogDebugMessage ($"'emulator -list-avds' returned exit code: {exitCode}"); + } + } catch (Exception ex) { + Log.LogDebugMessage ($"Failed to run 'emulator -list-avds': {ex.Message}"); + } + + return emulators; + } + + /// + /// Queries the emulator for its AVD name using 'adb -s <serial> emu avd name'. + /// Returns the raw AVD name (not formatted). + /// + protected virtual string? GetEmulatorAvdName (string serial) + { + try { + var adbPath = Path.Combine (ToolPath, ToolExe); + var outputLines = new List (); + + var exitCode = MonoAndroidHelper.RunProcess ( + adbPath, + $"-s {serial} emu avd name", + Log, + onOutput: (sender, e) => { + if (!e.Data.IsNullOrEmpty ()) { + outputLines.Add (e.Data); + } + }, + logWarningOnFailure: false + ); + + if (exitCode == 0 && outputLines.Count > 0) { + var avdName = outputLines [0].Trim (); + // Verify it's not the "OK" response + if (!avdName.IsNullOrEmpty () && !avdName.Equals ("OK", StringComparison.OrdinalIgnoreCase)) { + Log.LogDebugMessage ($"Emulator {serial} has AVD name: {avdName}"); + return avdName; + } + } + } catch (Exception ex) { + Log.LogDebugMessage ($"Failed to get AVD name for {serial}: {ex.Message}"); + } + + return null; + } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/AndroidRuntime.cs b/src/Xamarin.Android.Build.Tasks/Utilities/AndroidRuntime.cs index 2e2fa66709f..89592e29265 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/AndroidRuntime.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/AndroidRuntime.cs @@ -3,7 +3,7 @@ namespace Xamarin.Android.Tasks; public enum AndroidRuntime { - MonoVM, - CoreCLR, - NativeAOT, + MonoVM, + CoreCLR, + NativeAOT, } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/JsonExtensions.cs b/src/Xamarin.Android.Build.Tasks/Utilities/JsonExtensions.cs index bf189ceaded..408a25d8a6a 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/JsonExtensions.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/JsonExtensions.cs @@ -6,83 +6,83 @@ public static class JsonExtensions { - public static JsonNode? Merge (this JsonNode? jsonBase, JsonNode? jsonMerge) - { - if (jsonBase == null || jsonMerge == null) - return jsonBase; + public static JsonNode? Merge (this JsonNode? jsonBase, JsonNode? jsonMerge) + { + if (jsonBase == null || jsonMerge == null) + return jsonBase; - switch (jsonBase) - { - case JsonObject jsonBaseObj when jsonMerge is JsonObject jsonMergeObj: { - var mergeNodesArray = new KeyValuePair [jsonMergeObj.Count]; - int index = 0; - foreach (var prop in jsonMergeObj) { - mergeNodesArray [index++] = prop; - } - jsonMergeObj.Clear (); + switch (jsonBase) + { + case JsonObject jsonBaseObj when jsonMerge is JsonObject jsonMergeObj: { + var mergeNodesArray = new KeyValuePair [jsonMergeObj.Count]; + int index = 0; + foreach (var prop in jsonMergeObj) { + mergeNodesArray [index++] = prop; + } + jsonMergeObj.Clear (); - foreach (var prop in mergeNodesArray) { - jsonBaseObj [prop.Key] = jsonBaseObj [prop.Key] switch { - JsonObject jsonBaseChildObj when prop.Value is JsonObject jsonMergeChildObj => jsonBaseChildObj.Merge (jsonMergeChildObj), - JsonArray jsonBaseChildArray when prop.Value is JsonArray jsonMergeChildArray => jsonBaseChildArray.Merge (jsonMergeChildArray), - _ => prop.Value - }; - } - break; - } - case JsonArray jsonBaseArray when jsonMerge is JsonArray jsonMergeArray: { - var mergeNodesArray = new JsonNode? [jsonMergeArray.Count]; - int index = 0; - foreach (var mergeNode in jsonMergeArray) { - mergeNodesArray [index++] = mergeNode; - } - jsonMergeArray.Clear (); - foreach (var mergeNode in mergeNodesArray) { - jsonBaseArray.Add (mergeNode); - } - break; - } - default: - throw new ArgumentException ($"The JsonNode type [{jsonBase.GetType ().Name}] is incompatible for merging with the target/base " + - $"type [{jsonMerge.GetType ().Name}]; merge requires the types to be the same."); - } - return jsonBase; - } + foreach (var prop in mergeNodesArray) { + jsonBaseObj [prop.Key] = jsonBaseObj [prop.Key] switch { + JsonObject jsonBaseChildObj when prop.Value is JsonObject jsonMergeChildObj => jsonBaseChildObj.Merge (jsonMergeChildObj), + JsonArray jsonBaseChildArray when prop.Value is JsonArray jsonMergeChildArray => jsonBaseChildArray.Merge (jsonMergeChildArray), + _ => prop.Value + }; + } + break; + } + case JsonArray jsonBaseArray when jsonMerge is JsonArray jsonMergeArray: { + var mergeNodesArray = new JsonNode? [jsonMergeArray.Count]; + int index = 0; + foreach (var mergeNode in jsonMergeArray) { + mergeNodesArray [index++] = mergeNode; + } + jsonMergeArray.Clear (); + foreach (var mergeNode in mergeNodesArray) { + jsonBaseArray.Add (mergeNode); + } + break; + } + default: + throw new ArgumentException ($"The JsonNode type [{jsonBase.GetType ().Name}] is incompatible for merging with the target/base " + + $"type [{jsonMerge.GetType ().Name}]; merge requires the types to be the same."); + } + return jsonBase; + } - public static JsonNode? ToNode (this JsonElement element) - { - switch (element.ValueKind) { - case JsonValueKind.Object: - var obj = new JsonObject (); - foreach (JsonProperty prop in element.EnumerateObject()) { - obj [prop.Name] = prop.Value.ToNode (); - } - return obj; + public static JsonNode? ToNode (this JsonElement element) + { + switch (element.ValueKind) { + case JsonValueKind.Object: + var obj = new JsonObject (); + foreach (JsonProperty prop in element.EnumerateObject()) { + obj [prop.Name] = prop.Value.ToNode (); + } + return obj; - case JsonValueKind.Array: - var arr = new JsonArray(); - foreach (JsonElement item in element.EnumerateArray ()) { - arr.Add (item.ToNode ()); - } - return arr; + case JsonValueKind.Array: + var arr = new JsonArray(); + foreach (JsonElement item in element.EnumerateArray ()) { + arr.Add (item.ToNode ()); + } + return arr; - case JsonValueKind.String: - return element.GetString (); + case JsonValueKind.String: + return element.GetString (); - case JsonValueKind.Number: - return element.TryGetInt32 (out int intValue) ? intValue : element.GetDouble (); + case JsonValueKind.Number: + return element.TryGetInt32 (out int intValue) ? intValue : element.GetDouble (); - case JsonValueKind.True: - return true; + case JsonValueKind.True: + return true; - case JsonValueKind.False: - return false; + case JsonValueKind.False: + return false; - case JsonValueKind.Null: - return null; + case JsonValueKind.Null: + return null; - default: - throw new NotSupportedException ($"Unsupported JSON value kind: {element.ValueKind}"); - } - } + default: + throw new NotSupportedException ($"Unsupported JSON value kind: {element.ValueKind}"); + } + } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrTypeCache.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrTypeCache.cs index 500ab392203..d1d22a8622d 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrTypeCache.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrTypeCache.cs @@ -9,32 +9,32 @@ namespace Xamarin.Android.Tasks.LLVMIR; /// class LlvmIrTypeCache { - readonly Dictionary pointerAttributes = []; - readonly Dictionary assemblerAttributes = []; + readonly Dictionary pointerAttributes = []; + readonly Dictionary assemblerAttributes = []; - /// - /// Gets the for the specified member, using caching to improve performance. - /// - /// The member info to get the attribute for. - /// The if present; otherwise, null. - public NativePointerAttribute? GetNativePointerAttribute (MemberInfo mi) - { - if (!pointerAttributes.TryGetValue (mi, out var attr)) { - pointerAttributes[mi] = attr = mi.GetCustomAttribute (); - } - return attr; - } + /// + /// Gets the for the specified member, using caching to improve performance. + /// + /// The member info to get the attribute for. + /// The if present; otherwise, null. + public NativePointerAttribute? GetNativePointerAttribute (MemberInfo mi) + { + if (!pointerAttributes.TryGetValue (mi, out var attr)) { + pointerAttributes[mi] = attr = mi.GetCustomAttribute (); + } + return attr; + } - /// - /// Gets the for the specified member, using caching to improve performance. - /// - /// The member info to get the attribute for. - /// The if present; otherwise, null. - public NativeAssemblerAttribute? GetNativeAssemblerAttribute (MemberInfo mi) - { - if (!assemblerAttributes.TryGetValue (mi, out var attr)) { - assemblerAttributes[mi] = attr = mi.GetCustomAttribute (); - } - return attr; - } + /// + /// Gets the for the specified member, using caching to improve performance. + /// + /// The member info to get the attribute for. + /// The if present; otherwise, null. + public NativeAssemblerAttribute? GetNativeAssemblerAttribute (MemberInfo mi) + { + if (!assemblerAttributes.TryGetValue (mi, out var attr)) { + assemblerAttributes[mi] = attr = mi.GetCustomAttribute (); + } + return attr; + } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MSBuildLinkContext.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MSBuildLinkContext.cs index 388d27b07fa..f31ac199292 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MSBuildLinkContext.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MSBuildLinkContext.cs @@ -10,19 +10,19 @@ namespace Xamarin.Android.Tasks; public class MSBuildLinkContext : LinkContext { - public new DirectoryAssemblyResolver Resolver { get; private set; } - readonly TaskLoggingHelper logger; + public new DirectoryAssemblyResolver Resolver { get; private set; } + readonly TaskLoggingHelper logger; - public MSBuildLinkContext (DirectoryAssemblyResolver resolver, TaskLoggingHelper logger) - : base (resolver) - { - Resolver = resolver; - this.logger = logger; - } + public MSBuildLinkContext (DirectoryAssemblyResolver resolver, TaskLoggingHelper logger) + : base (resolver) + { + Resolver = resolver; + this.logger = logger; + } - public override void LogMessage (string message) => logger.LogDebugMessage (message); + public override void LogMessage (string message) => logger.LogDebugMessage (message); - public override void LogWarning (string code, string message) => logger.LogCodedWarning (code, message); + public override void LogWarning (string code, string message) => logger.LogCodedWarning (code, message); - public override void LogError (string code, string message) => logger.LogCodedError (code, message); + public override void LogError (string code, string message) => logger.LogCodedError (code, message); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEntryExtensions.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEntryExtensions.cs index 2ccc31187c0..70d6119c447 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEntryExtensions.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEntryExtensions.cs @@ -2,8 +2,8 @@ using System; using System.IO.Compression; public static class ZipArchiveEntryExtensions { - public static bool IsDirectory (this ZipArchiveEntry entry) - { - return entry.Length == 0 && entry.FullName.EndsWith ("/", StringComparison.Ordinal); - } -} \ No newline at end of file + public static bool IsDirectory (this ZipArchiveEntry entry) + { + return entry.Length == 0 && entry.FullName.EndsWith ("/", StringComparison.Ordinal); + } +}