[siminstaller] Adjust according to recent Xcode changes. (#15449)

The url to download the list of simulators that are available has changed, and
the format changed slightly as well.
This commit is contained in:
Rolf Bjarne Kvinge 2022-07-11 23:27:52 +02:00 коммит произвёл GitHub
Родитель 5b3c844ba9
Коммит 4c53f68d8d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 72 добавлений и 19 удалений

Просмотреть файл

@ -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;
@ -83,7 +84,7 @@ namespace xsiminstaller {
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) },
@ -134,22 +135,49 @@ namespace xsiminstaller {
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) {
@ -169,13 +197,14 @@ namespace xsiminstaller {
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");
@ -200,7 +229,14 @@ namespace xsiminstaller {
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}";
}
double.TryParse (fileSizeNode?.InnerText, out var parsedFileSize);
var fileSize = (long) parsedFileSize;
@ -405,11 +441,28 @@ namespace xsiminstaller {
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