Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
[siminstaller] Adjust according to recent Xcode changes.
The url to download the list of simulators that are available has changed, and
the format changed slightly as well.
  • Loading branch information
rolfbjarne committed Jul 11, 2022
commit 70b399a9f946dac5e8b745c9b939d6bed428c0b9
91 changes: 72 additions & 19 deletions tools/siminstaller/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.IO;
Expand Down Expand Up @@ -83,7 +84,7 @@ public static int Main (string [] args)
var only_check = false;
var force = false;
var printHelp = false;

var os = new OptionSet {
{ "xcode=", "The Xcode.app to use", (v) => xcode_app = v },
{ "install=", "ID of simulator to install. Can be repeated multiple times.", (v) => install.Add (v) },
Expand Down Expand Up @@ -134,22 +135,49 @@ public static int Main (string [] args)

xcodeVersion = xcodeVersion.Insert (xcodeVersion.Length - 2, ".");
xcodeVersion = xcodeVersion.Insert (xcodeVersion.Length - 1, ".");
var url = $"https://devimages-cdn.apple.com/downloads/xcode/simulators/index-{xcodeVersion}-{xcodeUuid}.dvtdownloadableindex";
var uri = new Uri (url);
var tmpfile = Path.Combine (TempDirectory, Path.GetFileName (uri.LocalPath));

var indexName = $"index-{xcodeVersion}-{xcodeUuid}.dvtdownloadableindex";
var tmpfile = Path.Combine (TempDirectory, indexName);
if (!File.Exists (tmpfile)) {
var wc = new WebClient ();
try {
if (verbose > 0)
Console.WriteLine ($"Downloading '{uri}'");
wc.DownloadFile (uri, tmpfile);
} catch (Exception ex) {
// 403 means 404
if (ex is WebException we && (we.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.Forbidden) {
Console.WriteLine ($"Failed to download {url}: Not found"); // Apple's servers return a 403 if the file doesn't exist, which can be quite confusing, so show a better error.
} else {
Console.WriteLine ($"Failed to download {url}: {ex}");
// Try multiple urls
var urls = new string [] {
$"https://devimages-cdn.apple.com/downloads/xcode/simulators/{indexName}",
/*
* The following url was found while debugging Xcode, the "index2" part is actually hardcoded:
*
* DVTFoundation`-[DVTDownloadableIndexSource identifier]:
* 0x103db478d <+0>: pushq %rbp
* 0x103db478e <+1>: movq %rsp, %rbp
* 0x103db4791 <+4>: leaq 0x53f008(%rip), %rax ; @"index2"
* 0x103db4798 <+11>: popq %rbp
* 0x103db4799 <+12>: retq
*
*/
"https://devimages-cdn.apple.com/downloads/xcode/simulators/index2.dvtdownloadableindex",
};
var anyFailures = false;
foreach (var url in urls) {
var uri = new Uri (url);
var wc = new WebClient ();
try {
if (verbose > 0)
Console.WriteLine ($"Downloading '{uri}'");
else if (anyFailures)
Console.WriteLine ($"Attempting fallback url '{uri}'");
wc.DownloadFile (uri, tmpfile);
} catch (Exception ex) {
File.Delete (tmpfile); // Make sure there are no downloaded remnants
// 403 means 404
if (ex is WebException we && (we.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.Forbidden) {
Console.WriteLine ($"Failed to download {url}: Not found"); // Apple's servers return a 403 if the file doesn't exist, which can be quite confusing, so show a better error.
} else {
Console.WriteLine ($"Failed to download {url}: {ex}");
}
anyFailures = true;
}
}

if (!File.Exists (tmpfile)) {
// We couldn't download the list of simulators, but the simulator(s) we were requested to install might already be installed.
// Don't fail in that case (we'd miss any potential updates, but that's probably not too bad).
if (install.Count > 0) {
Expand All @@ -169,13 +197,14 @@ public static int Main (string [] args)
return 1;
}
}
if (!TryExecuteAndCapture ("plutil", $"-convert xml1 -o - '{tmpfile}'", out var xml))

if (!TryExecuteAndCapture ("plutil", $"-convert xml1 -o - '{tmpfile}'", out var xml))
return 1;

var doc = new XmlDocument ();
doc.LoadXml (xml);

var downloadables = doc.SelectNodes ("//plist/dict/key[text()='downloadables']/following-sibling::array/dict");
var downloadables = doc.SelectNodes ("//plist/dict/key[text()='downloadables']/following-sibling::array[1]/dict");
foreach (XmlNode downloadable in downloadables) {
var nameNode = downloadable.SelectSingleNode ("key[text()='name']/following-sibling::string");
var versionNode = downloadable.SelectSingleNode ("key[text()='version']/following-sibling::string");
Expand All @@ -200,7 +229,14 @@ public static int Main (string [] args)

var name = Replace (nameNode.InnerText, dict);
var source = Replace (sourceNode.InnerText, dict);
var installPrefix = Replace (installPrefixNode.InnerText, dict);
var installPrefix = Replace (installPrefixNode?.InnerText, dict);

if (installPrefix is null) {
// This is just guesswork
var simRuntimeName = name.Replace (" Simulator", ".simruntime");
installPrefix = $"/Library/Developer/CoreSimulator/Profiles/Runtimes/{simRuntimeName}";
Copy link
Member

Choose a reason for hiding this comment

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

What should be the path created? i ported this to xharness but it's not working .. i m getting a path with a space not sure if it's right..

 Oct  3 10:52:12  installer[5885] <Info>: PackageKit: Registered bundle file:///Library/Developer/CoreSimulator/Profiles/Runtimes/iOS%2014.5.simruntime/Contents/Resources/RuntimeRoot/Applications/AMSEngagementViewService.app/ for uid 0

Copy link
Member Author

Choose a reason for hiding this comment

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

This logic might be wrong for the iOS 14.5 simulator, we only install the iOS 12.4 simulator (which works for us).

The space (%20) looks normal to me:

$ ls -la /Library/Developer/CoreSimulator/Profiles/Runtimes/
total 0
drwxr-xr-x  32 root  admin  1024 Sep 30 17:27 .
drwxr-xr-x   3 root  admin    96 Feb  8  2019 ..
drwxrwxr-t   3 root  admin    96 Jun  7 08:50 iOS 10.0.simruntime
drwxrwxr-t   3 root  admin    96 Jun  7 08:50 iOS 10.1.simruntime
drwxrwxr-t   3 root  admin    96 Jun  7 08:52 iOS 10.2.simruntime
drwxrwxr-t   3 root  admin    96 Jun  7 08:51 iOS 10.3.simruntime
drwxrwxr-t   3 root  admin    96 Jun  7 08:51 iOS 11.0.simruntime
drwxrwxr-t   3 root  admin    96 Jun  7 08:51 iOS 11.4.simruntime
drwxrwxr-t   3 root  admin    96 Jun  7 08:50 iOS 12.0.simruntime
drwxrwxr-t   3 root  admin    96 Jun  7 08:51 iOS 12.1.simruntime
[...]

@akoeplinger pointed out that Xcode does something different with newer simulators (mounts a dmg in /Library/Developer/CoreSimulator/Volumes/) - but I have no idea how to download the dmg and make Xcode mount it.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah but it didn't work for us with iOS 12.4 .. Did you try on a new machine works for you ?

Process ID: 6209
[1.0.0-prerelease.22480.2+db24a6d8a7fd009688425151e3da19cb5c4ca47f] XHarness command issued: apple test --app=/Users/runner/work/1/s/src/Controls/tests/DeviceTests/bin/Release/net6.0-ios/iossimulator-x64/Microsoft.Maui.Controls.DeviceTests.app --targets=ios-simulator-64_12.4 --output-directory=/Users/runner/work/1/a/test-results --verbosity=Debug
info: Preparing run for ios-simulator-64_12.4
info: Looking for available ios-simulator-64_12.4 simulators..
dbug: Looking for available ios-simulator-64_12.4 simulators. Storing logs into list-ios-simulator-64_12.4-20221003_105020.log
fail: Failed to find/create suitable simulator
XHarness exit code: 81 (DEVICE_NOT_FOUND)
An error occurred when executing task 'Test'.

I installed via Xcode 13.7 and u see it there

ruimarinho@iMSFT-MacBookPro Runtimes % ls -la /Library/Developer/CoreSimulator/Profiles/Runtimes/
total 0
drwxr-xr-x  3 root  wheel  96 Oct  3 12:28 .
drwxr-xr-x  3 root  wheel  96 Oct  3 12:28 ..
drwxrwxr-t  3 root  admin  96 Oct  3 12:23 iOS 13.7.simruntime

}

double.TryParse (fileSizeNode?.InnerText, out var parsedFileSize);
var fileSize = (long) parsedFileSize;

Expand Down Expand Up @@ -405,11 +441,28 @@ static bool Install (string source, long fileSize, string installPrefix)
return true;
}

static string Replace (string value, Dictionary<string, string> replacements)
[return: NotNullIfNotNull ("value")]
static string? Replace ( string? value, Dictionary<string, string> replacements)
{
if (value is null)
return null;
foreach (var kvp in replacements)
value = value.Replace ($"$({kvp.Key})", kvp.Value);
return value;
}
}
}

#if !NET // the below attributes are no longer needed once we switch to .NET
namespace System.Diagnostics.CodeAnalysis {
[AttributeUsage (AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
internal sealed class NotNullIfNotNullAttribute : Attribute {
public string ParameterName { get; }

public NotNullIfNotNullAttribute (string parameterName)
{
ParameterName = parameterName;
}
}
}
#endif // !NET