Skip to content

Commit 1680d9d

Browse files
authored
Merge pull request #211 from sfoslund/SupportArm64
Adding arm64 support
2 parents 9007e72 + f3da7a9 commit 1680d9d

File tree

5 files changed

+111
-44
lines changed

5 files changed

+111
-44
lines changed

src/dotnet-core-uninstall/MacOs/FileSystemExplorer.cs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using System.Linq;
8+
using System.Runtime.InteropServices;
89
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo;
910
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo.Versioning;
1011
using Microsoft.DotNet.Tools.Uninstall.Shared.Configs;
@@ -16,25 +17,53 @@ internal class FileSystemExplorer : IBundleCollector
1617
private static readonly string DotNetInstallPath = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DOTNET_INSTALL_DIR")) ?
1718
Path.Combine("/", "usr", "local", "share", "dotnet") :
1819
Environment.GetEnvironmentVariable("DOTNET_INSTALL_DIR");
19-
private static readonly string DotNetSdkInstallPath = Path.Combine(DotNetInstallPath, "sdk");
20-
private static readonly string DotNetRuntimeInstallPath = Path.Combine(DotNetInstallPath, "shared", "Microsoft.NETCore.App");
21-
private static readonly string DotNetAspAllInstallPath = Path.Combine(DotNetInstallPath, "shared", "Microsoft.AspNetCore.All");
22-
private static readonly string DotNetAspAppInstallPath = Path.Combine(DotNetInstallPath, "shared", "Microsoft.AspNetCore.App");
23-
private static readonly string DotNetHostFxrInstallPath = Path.Combine(DotNetInstallPath, "host", "fxr");
20+
private static readonly string EmulatedDotNetInstallPath = Path.Combine(DotNetInstallPath, "x64");
21+
private static string DotNetSdkInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "sdk");
22+
private static string DotNetRuntimeInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "shared", "Microsoft.NETCore.App");
23+
private static string DotNetAspAllInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "shared", "Microsoft.AspNetCore.All");
24+
private static string DotNetAspAppInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "shared", "Microsoft.AspNetCore.App");
25+
private static string DotNetHostFxrInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "host", "fxr");
2426

2527
public virtual IEnumerable<Bundle> GetAllInstalledBundles()
2628
{
27-
var sdks = GetInstalledBundles<SdkVersion>(DotNetSdkInstallPath);
29+
var nativeArch = IsMacx64Installation(DotNetInstallPath) ? BundleArch.X64 : BundleArch.Arm64;
30+
var sdks = GetInstalledBundles<SdkVersion>(nativeArch, DotNetSdkInstallPath(DotNetInstallPath));
2831
var runtimes = GetInstalledBundles<RuntimeVersion>(
29-
DotNetRuntimeInstallPath,
30-
DotNetAspAllInstallPath,
31-
DotNetAspAppInstallPath,
32-
DotNetHostFxrInstallPath);
32+
nativeArch,
33+
DotNetRuntimeInstallPath(DotNetInstallPath),
34+
DotNetAspAllInstallPath(DotNetInstallPath),
35+
DotNetAspAppInstallPath(DotNetInstallPath),
36+
DotNetHostFxrInstallPath(DotNetInstallPath));
37+
38+
if (Directory.Exists(EmulatedDotNetInstallPath))
39+
{
40+
sdks = sdks.Concat(GetInstalledBundles<SdkVersion>(BundleArch.X64, DotNetSdkInstallPath(EmulatedDotNetInstallPath)));
41+
runtimes = runtimes.Concat(GetInstalledBundles<RuntimeVersion>(
42+
BundleArch.X64,
43+
DotNetRuntimeInstallPath(DotNetInstallPath),
44+
DotNetAspAllInstallPath(DotNetInstallPath),
45+
DotNetAspAppInstallPath(DotNetInstallPath),
46+
DotNetHostFxrInstallPath(DotNetInstallPath)));
47+
}
3348

3449
return sdks.Concat(runtimes).ToList();
3550
}
3651

37-
private static IEnumerable<Bundle> GetInstalledBundles<TBundleVersion>(params string[] paths)
52+
private static bool IsMacx64Installation(string path)
53+
{
54+
try
55+
{
56+
var versionDirs = Directory.GetDirectories(Path.Combine(path, "sdk"));
57+
var rids = File.ReadAllText(Path.Combine(versionDirs[0], "NETCoreSdkRuntimeIdentifierChain.txt"));
58+
return !rids.Contains("arm64");
59+
}
60+
catch
61+
{
62+
return true;
63+
}
64+
}
65+
66+
private static IEnumerable<Bundle> GetInstalledBundles<TBundleVersion>(BundleArch arch, params string[] paths)
3867
where TBundleVersion : BundleVersion, IComparable<TBundleVersion>, new()
3968
{
4069
string bundleTypeString;
@@ -50,7 +79,7 @@ private static IEnumerable<Bundle> GetInstalledBundles<TBundleVersion>(params st
5079
.GroupBy(tuple => tuple.Version)
5180
.Select(group => Bundle.From(
5281
group.First().Version,
53-
BundleArch.X64,
82+
arch,
5483
GetUninstallCommand(group.Select(tuple => tuple.Path)),
5584
string.Format(LocalizableStrings.MacOsBundleDisplayNameFormat, bundleTypeString, group.First().Version.ToString())));
5685
}

src/dotnet-core-uninstall/Shared/BundleInfo/BundleArch.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo
99
internal enum BundleArch
1010
{
1111
X86 = 0x1,
12-
X64 = 0x2
12+
X64 = 0x2,
13+
Arm64 = 0x3
1314
}
1415
}

src/dotnet-core-uninstall/Windows/RegistryQuery.cs

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Linq;
7-
using System.Runtime.InteropServices;
87
using System.Text.RegularExpressions;
98
using Microsoft.DotNet.Tools.Uninstall.MacOs;
109
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo;
@@ -25,25 +24,8 @@ public IEnumerable<Bundle> GetInstalledBundles()
2524

2625
public virtual IEnumerable<Bundle> GetAllInstalledBundles()
2726
{
28-
var uninstalls = Registry.LocalMachine
29-
.OpenSubKey("SOFTWARE");
30-
31-
if (RuntimeInformation.ProcessArchitecture == Architecture.X64 || RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
32-
{
33-
uninstalls = uninstalls.OpenSubKey("WOW6432Node");
34-
}
35-
36-
uninstalls = uninstalls
37-
.OpenSubKey("Microsoft")
38-
.OpenSubKey("Windows")
39-
.OpenSubKey("CurrentVersion")
40-
.OpenSubKey("Uninstall");
41-
42-
var names = uninstalls.GetSubKeyNames();
43-
44-
var bundles = names
45-
.Select(name => uninstalls.OpenSubKey(name))
46-
.Where(bundle => IsNetCoreBundle(bundle));
27+
var bundles = GetNetCoreBundleKeys(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64));
28+
bundles = bundles.Concat(GetNetCoreBundleKeys(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)));
4729

4830
var wrappedBundles = bundles
4931
.Select(bundle => WrapRegistryKey(bundle))
@@ -53,6 +35,26 @@ public virtual IEnumerable<Bundle> GetAllInstalledBundles()
5335
return wrappedBundles;
5436
}
5537

38+
private IEnumerable<RegistryKey> GetNetCoreBundleKeys(RegistryKey uninstallKey)
39+
{
40+
try
41+
{
42+
var uninstalls = uninstallKey
43+
.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
44+
45+
var names = uninstalls.GetSubKeyNames();
46+
47+
return names
48+
.Select(name => uninstalls.OpenSubKey(name))
49+
.Where(bundle => IsNetCoreBundle(bundle));
50+
}
51+
catch
52+
{
53+
return Enumerable.Empty<RegistryKey>();
54+
}
55+
56+
}
57+
5658
private static bool IsNetCoreBundle(RegistryKey uninstallKey)
5759
{
5860
if (uninstallKey == null)
@@ -75,7 +77,8 @@ internal static bool IsNetCoreBundle(string displayName, string displayVersion,
7577
((displayName.IndexOf(".NET", StringComparison.OrdinalIgnoreCase) >= 0) ||
7678
(displayName.IndexOf(".NET Runtime", StringComparison.OrdinalIgnoreCase) >= 0) ||
7779
(displayName.IndexOf(".NET SDK", StringComparison.OrdinalIgnoreCase) >= 0) ||
78-
(displayName.IndexOf("Dotnet Shared Framework for Windows Desktop", StringComparison.OrdinalIgnoreCase) >= 0)) &&
80+
(displayName.IndexOf("Dotnet Shared Framework for Windows Desktop", StringComparison.OrdinalIgnoreCase) >= 0) ||
81+
(displayName.IndexOf("Windows Desktop Runtime", StringComparison.OrdinalIgnoreCase) >= 0)) &&
7982
(!String.IsNullOrEmpty(uninstallString)) &&
8083
(uninstallString.IndexOf(".exe", StringComparison.OrdinalIgnoreCase) >= 0) &&
8184
(uninstallString.IndexOf("msiexec", StringComparison.OrdinalIgnoreCase) < 0) &&
@@ -86,7 +89,7 @@ internal static bool IsNetCoreBundle(string displayName, string displayVersion,
8689
private static Bundle WrapRegistryKey(RegistryKey registryKey)
8790
{
8891
var displayName = registryKey.GetValue("DisplayName") as string;
89-
var uninstallCommand = registryKey.GetValue("QuietUninstallString") as string;
92+
var uninstallCommand = registryKey.GetValue("QuietUninstallString") as string ?? registryKey.GetValue("UninstallString") as string;
9093
var bundleCachePath = registryKey.GetValue("BundleCachePath") as string;
9194

9295
var version = GetBundleVersion(displayName, uninstallCommand, bundleCachePath);
@@ -103,11 +106,15 @@ private static Bundle WrapRegistryKey(RegistryKey registryKey)
103106
public static BundleVersion GetBundleVersion(string displayName, string uninstallString, string bundleCachePath)
104107
{
105108
var versionString = Regexes.VersionDisplayNameRegex.Match(displayName)?.Value ?? string.Empty;
106-
var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);
107-
var hasAuxVersion = cachePathMatch.Groups[Regexes.AuxVersionGroupName].Success;
108-
var footnote = hasAuxVersion ?
109-
string.Format(LocalizableStrings.HostingBundleFootnoteFormat, displayName, versionString) :
110-
null;
109+
string footnote = null;
110+
if (bundleCachePath != null)
111+
{
112+
var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);
113+
var hasAuxVersion = cachePathMatch.Groups[Regexes.AuxVersionGroupName].Success;
114+
footnote = hasAuxVersion ?
115+
string.Format(LocalizableStrings.HostingBundleFootnoteFormat, displayName, versionString) :
116+
null;
117+
}
111118

112119
try
113120
{
@@ -146,20 +153,35 @@ private static BundleArch GetBundleArch(string displayName, string bundleCachePa
146153
{
147154
const string x64String = "x64";
148155
const string x86String = "x86";
156+
const string arm64String = "arm64";
149157

150-
var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);
151-
152-
var archString = cachePathMatch.Groups[Regexes.ArchGroupName].Value;
158+
string archString = null;
159+
if (bundleCachePath != null)
160+
{
161+
var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);
162+
archString = cachePathMatch.Groups[Regexes.ArchGroupName].Value;
163+
}
153164

154165
if (string.IsNullOrEmpty(archString))
155166
{
156-
archString = displayName.Contains(x64String) ? x64String : displayName.Contains(x86String) ? x86String : string.Empty;
167+
archString = displayName.Contains(x64String) ?
168+
x64String :
169+
displayName.Contains(x86String) ? x86String : string.Empty;
170+
171+
archString = archString switch
172+
{
173+
string a when a.Contains(x64String) => x64String,
174+
string b when b.Contains(x86String) => x86String,
175+
string b when b.Contains(arm64String) => arm64String,
176+
_ => string.Empty
177+
};
157178
}
158179

159180
switch (archString)
160181
{
161182
case x64String: return BundleArch.X64;
162183
case x86String: return BundleArch.X86;
184+
case arm64String: return BundleArch.Arm64;
163185
case "": return BundleArch.X64 | BundleArch.X86;
164186
default: throw new ArgumentException();
165187
}

test/dotnet-core-uninstall.Tests/Shared/BundleInfo/BundleTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ public static IEnumerable<object[]> GetDataForTestFrom()
107107
TestUninstallCommand1,
108108
TestDisplayName1
109109
};
110+
111+
yield return new object[]
112+
{
113+
TestHostingBundleVersion1,
114+
BundleArch.Arm64,
115+
TestUninstallCommand1,
116+
TestDisplayName1
117+
};
110118
}
111119

112120
[Theory]
@@ -186,6 +194,12 @@ public static IEnumerable<object[]> GetDataForTestToString()
186194
TestRuntimeVersion1,
187195
BundleArch.X86
188196
};
197+
198+
yield return new object[]
199+
{
200+
TestRuntimeVersion1,
201+
BundleArch.Arm64
202+
};
189203
}
190204

191205
[Theory]

test/dotnet-core-uninstall.Tests/Windows/RegistryQueryTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class RegistryQueryTests
4545
[InlineData("Microsoft .NET Core 3.0.0 Preview 6 Build 19307.2 - Windows Server Hosting")]
4646
[InlineData("Microsoft .NET Core 3.1.0 Preview 1 Build preview1.19307.20 - Windows Server Hosting")]
4747
[InlineData("Microsoft .NET 5.0.9 - Windows Server Hosting")]
48+
[InlineData("Microsoft .NET SDK 5.0.100 (arm64)")]
4849
internal void TestIsNetCoreBundleAccept(string input)
4950
{
5051
RegistryQuery.IsNetCoreBundle(input, "0.0", "mockuninstall.exe", "0.0")

0 commit comments

Comments
 (0)