Skip to content

Commit 5121ce7

Browse files
rmarinhoCopilot
andauthored
Add runtime management APIs (#159)
* Add runtime management APIs Add RuntimeService for listing simulator runtimes and downloading platform runtimes via xcodebuild. Includes SimctlOutputParser for parsing xcrun simctl JSON output (shared with SimulatorService). Download pattern from ClientTools.Platform: xcodebuild -downloadPlatform. Runtime listing reuses SimctlOutputParser.ParseRuntimes with filtering by platform and availability. Adds System.Text.Json dependency for netstandard2.0 target. Closes #150 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback: xcrun xcodebuild, version param, tests - Use xcrun xcodebuild instead of resolving xcodebuild path inside Xcode bundle - Remove ResolveXcodebuild and XcodebuildRelativePath (rolfbjarne feedback) - Add version parameter to DownloadPlatform (-buildVersion flag) - Add input validation to ListByPlatform - Catch InvalidOperationException alongside Win32Exception - Add RuntimeServiceTests with smoke tests - Adopt file-scoped namespaces and flat usings - Sync SimctlOutputParser and tests.csproj from PR #158 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use shared SimCtl class and add exception logging to parsers - Refactor RuntimeService to use shared SimCtl class instead of inline RunSimctl - Add subprocess logging to DownloadPlatform (xcrun xcodebuild) - Add optional ICustomLogger to ParseDevices/ParseRuntimes for exception logging Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 10a0c3c commit 5121ce7

2 files changed

Lines changed: 146 additions & 0 deletions

File tree

Xamarin.MacDev/RuntimeService.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Xamarin.MacDev.Models;
8+
9+
#nullable enable
10+
11+
namespace Xamarin.MacDev;
12+
13+
/// <summary>
14+
/// Manages simulator runtimes via <c>xcrun simctl</c> and <c>xcrun xcodebuild</c>.
15+
/// </summary>
16+
public class RuntimeService {
17+
18+
static readonly string XcrunPath = "/usr/bin/xcrun";
19+
20+
readonly ICustomLogger log;
21+
readonly SimCtl simctl;
22+
23+
public RuntimeService (ICustomLogger log)
24+
{
25+
this.log = log ?? throw new ArgumentNullException (nameof (log));
26+
simctl = new SimCtl (log);
27+
}
28+
29+
/// <summary>
30+
/// Lists installed simulator runtimes. Optionally filters by availability.
31+
/// </summary>
32+
public List<SimulatorRuntimeInfo> List (bool availableOnly = false)
33+
{
34+
var json = simctl.Run ("list", "runtimes", "--json");
35+
if (json is null)
36+
return new List<SimulatorRuntimeInfo> ();
37+
38+
var runtimes = SimctlOutputParser.ParseRuntimes (json, log);
39+
40+
if (availableOnly)
41+
runtimes.RemoveAll (r => !r.IsAvailable);
42+
43+
log.LogInfo ("Found {0} simulator runtime(s).", runtimes.Count);
44+
return runtimes;
45+
}
46+
47+
/// <summary>
48+
/// Lists runtimes for a specific platform (e.g. "iOS", "tvOS", "watchOS", "visionOS").
49+
/// </summary>
50+
public List<SimulatorRuntimeInfo> ListByPlatform (string platform, bool availableOnly = false)
51+
{
52+
if (string.IsNullOrEmpty (platform))
53+
throw new ArgumentException ("Platform must not be null or empty.", nameof (platform));
54+
55+
var all = List (availableOnly);
56+
return all.Where (r => string.Equals (r.Platform, platform, StringComparison.OrdinalIgnoreCase)).ToList ();
57+
}
58+
59+
/// <summary>
60+
/// Downloads a platform runtime using <c>xcrun xcodebuild -downloadPlatform</c>.
61+
/// </summary>
62+
/// <param name="platform">The platform to download (e.g. "iOS", "tvOS", "watchOS", "visionOS").</param>
63+
/// <param name="version">Optional specific version to download (e.g. "17.5").</param>
64+
/// <returns>True if the download command succeeded.</returns>
65+
public bool DownloadPlatform (string platform, string? version = null)
66+
{
67+
if (string.IsNullOrEmpty (platform))
68+
throw new ArgumentException ("Platform must not be null or empty.", nameof (platform));
69+
70+
log.LogInfo ("Downloading {0} platform runtime via xcodebuild...", platform);
71+
72+
try {
73+
var args = string.IsNullOrEmpty (version)
74+
? new [] { "xcodebuild", "-downloadPlatform", platform }
75+
: new [] { "xcodebuild", "-downloadPlatform", platform, "-buildVersion", version! };
76+
77+
log.LogInfo ("Executing: {0} {1}", XcrunPath, string.Join (" ", args));
78+
var (exitCode, _, stderr) = ProcessUtils.Exec (XcrunPath, args);
79+
if (exitCode != 0) {
80+
log.LogInfo ("xcodebuild -downloadPlatform {0} failed (exit {1}): {2}", platform, exitCode, stderr.Trim ());
81+
return false;
82+
}
83+
84+
log.LogInfo ("Successfully downloaded {0} platform runtime.", platform);
85+
return true;
86+
} catch (System.ComponentModel.Win32Exception ex) {
87+
log.LogInfo ("Could not run xcodebuild: {0}", ex.Message);
88+
return false;
89+
} catch (InvalidOperationException ex) {
90+
log.LogInfo ("Could not run xcodebuild: {0}", ex.Message);
91+
return false;
92+
}
93+
}
94+
}

tests/RuntimeServiceTests.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using NUnit.Framework;
6+
using Xamarin.MacDev;
7+
8+
#nullable enable
9+
10+
namespace tests;
11+
12+
[TestFixture]
13+
public class RuntimeServiceTests {
14+
15+
[Test]
16+
public void Constructor_ThrowsOnNullLogger ()
17+
{
18+
Assert.Throws<ArgumentNullException> (() => new RuntimeService (null!));
19+
}
20+
21+
[Test]
22+
public void ListByPlatform_ThrowsOnNullPlatform ()
23+
{
24+
var svc = new RuntimeService (ConsoleLogger.Instance);
25+
Assert.Throws<ArgumentException> (() => svc.ListByPlatform (null!));
26+
Assert.Throws<ArgumentException> (() => svc.ListByPlatform (""));
27+
}
28+
29+
[Test]
30+
public void DownloadPlatform_ThrowsOnNullPlatform ()
31+
{
32+
var svc = new RuntimeService (ConsoleLogger.Instance);
33+
Assert.Throws<ArgumentException> (() => svc.DownloadPlatform (null!));
34+
Assert.Throws<ArgumentException> (() => svc.DownloadPlatform (""));
35+
}
36+
37+
[Test]
38+
[Platform ("MacOsX")]
39+
public void List_DoesNotThrow ()
40+
{
41+
var svc = new RuntimeService (ConsoleLogger.Instance);
42+
Assert.DoesNotThrow (() => svc.List ());
43+
}
44+
45+
[Test]
46+
[Platform ("MacOsX")]
47+
public void ListByPlatform_DoesNotThrow ()
48+
{
49+
var svc = new RuntimeService (ConsoleLogger.Instance);
50+
Assert.DoesNotThrow (() => svc.ListByPlatform ("iOS"));
51+
}
52+
}

0 commit comments

Comments
 (0)