Skip to content

Commit

Permalink
Merge pull request #211 from sfoslund/SupportArm64
Browse files Browse the repository at this point in the history
Adding arm64 support
  • Loading branch information
sfoslund authored Nov 4, 2021
2 parents 9007e72 + f3da7a9 commit 1680d9d
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 44 deletions.
53 changes: 41 additions & 12 deletions src/dotnet-core-uninstall/MacOs/FileSystemExplorer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo;
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo.Versioning;
using Microsoft.DotNet.Tools.Uninstall.Shared.Configs;
Expand All @@ -16,25 +17,53 @@ internal class FileSystemExplorer : IBundleCollector
private static readonly string DotNetInstallPath = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DOTNET_INSTALL_DIR")) ?
Path.Combine("/", "usr", "local", "share", "dotnet") :
Environment.GetEnvironmentVariable("DOTNET_INSTALL_DIR");
private static readonly string DotNetSdkInstallPath = Path.Combine(DotNetInstallPath, "sdk");
private static readonly string DotNetRuntimeInstallPath = Path.Combine(DotNetInstallPath, "shared", "Microsoft.NETCore.App");
private static readonly string DotNetAspAllInstallPath = Path.Combine(DotNetInstallPath, "shared", "Microsoft.AspNetCore.All");
private static readonly string DotNetAspAppInstallPath = Path.Combine(DotNetInstallPath, "shared", "Microsoft.AspNetCore.App");
private static readonly string DotNetHostFxrInstallPath = Path.Combine(DotNetInstallPath, "host", "fxr");
private static readonly string EmulatedDotNetInstallPath = Path.Combine(DotNetInstallPath, "x64");
private static string DotNetSdkInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "sdk");
private static string DotNetRuntimeInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "shared", "Microsoft.NETCore.App");
private static string DotNetAspAllInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "shared", "Microsoft.AspNetCore.All");
private static string DotNetAspAppInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "shared", "Microsoft.AspNetCore.App");
private static string DotNetHostFxrInstallPath(string dotnetRootPath) => Path.Combine(dotnetRootPath, "host", "fxr");

public virtual IEnumerable<Bundle> GetAllInstalledBundles()
{
var sdks = GetInstalledBundles<SdkVersion>(DotNetSdkInstallPath);
var nativeArch = IsMacx64Installation(DotNetInstallPath) ? BundleArch.X64 : BundleArch.Arm64;
var sdks = GetInstalledBundles<SdkVersion>(nativeArch, DotNetSdkInstallPath(DotNetInstallPath));
var runtimes = GetInstalledBundles<RuntimeVersion>(
DotNetRuntimeInstallPath,
DotNetAspAllInstallPath,
DotNetAspAppInstallPath,
DotNetHostFxrInstallPath);
nativeArch,
DotNetRuntimeInstallPath(DotNetInstallPath),
DotNetAspAllInstallPath(DotNetInstallPath),
DotNetAspAppInstallPath(DotNetInstallPath),
DotNetHostFxrInstallPath(DotNetInstallPath));

if (Directory.Exists(EmulatedDotNetInstallPath))
{
sdks = sdks.Concat(GetInstalledBundles<SdkVersion>(BundleArch.X64, DotNetSdkInstallPath(EmulatedDotNetInstallPath)));
runtimes = runtimes.Concat(GetInstalledBundles<RuntimeVersion>(
BundleArch.X64,
DotNetRuntimeInstallPath(DotNetInstallPath),
DotNetAspAllInstallPath(DotNetInstallPath),
DotNetAspAppInstallPath(DotNetInstallPath),
DotNetHostFxrInstallPath(DotNetInstallPath)));
}

return sdks.Concat(runtimes).ToList();
}

private static IEnumerable<Bundle> GetInstalledBundles<TBundleVersion>(params string[] paths)
private static bool IsMacx64Installation(string path)
{
try
{
var versionDirs = Directory.GetDirectories(Path.Combine(path, "sdk"));
var rids = File.ReadAllText(Path.Combine(versionDirs[0], "NETCoreSdkRuntimeIdentifierChain.txt"));
return !rids.Contains("arm64");
}
catch
{
return true;
}
}

private static IEnumerable<Bundle> GetInstalledBundles<TBundleVersion>(BundleArch arch, params string[] paths)
where TBundleVersion : BundleVersion, IComparable<TBundleVersion>, new()
{
string bundleTypeString;
Expand All @@ -50,7 +79,7 @@ private static IEnumerable<Bundle> GetInstalledBundles<TBundleVersion>(params st
.GroupBy(tuple => tuple.Version)
.Select(group => Bundle.From(
group.First().Version,
BundleArch.X64,
arch,
GetUninstallCommand(group.Select(tuple => tuple.Path)),
string.Format(LocalizableStrings.MacOsBundleDisplayNameFormat, bundleTypeString, group.First().Version.ToString())));
}
Expand Down
3 changes: 2 additions & 1 deletion src/dotnet-core-uninstall/Shared/BundleInfo/BundleArch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo
internal enum BundleArch
{
X86 = 0x1,
X64 = 0x2
X64 = 0x2,
Arm64 = 0x3
}
}
84 changes: 53 additions & 31 deletions src/dotnet-core-uninstall/Windows/RegistryQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.DotNet.Tools.Uninstall.MacOs;
using Microsoft.DotNet.Tools.Uninstall.Shared.BundleInfo;
Expand All @@ -25,25 +24,8 @@ public IEnumerable<Bundle> GetInstalledBundles()

public virtual IEnumerable<Bundle> GetAllInstalledBundles()
{
var uninstalls = Registry.LocalMachine
.OpenSubKey("SOFTWARE");

if (RuntimeInformation.ProcessArchitecture == Architecture.X64 || RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
uninstalls = uninstalls.OpenSubKey("WOW6432Node");
}

uninstalls = uninstalls
.OpenSubKey("Microsoft")
.OpenSubKey("Windows")
.OpenSubKey("CurrentVersion")
.OpenSubKey("Uninstall");

var names = uninstalls.GetSubKeyNames();

var bundles = names
.Select(name => uninstalls.OpenSubKey(name))
.Where(bundle => IsNetCoreBundle(bundle));
var bundles = GetNetCoreBundleKeys(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64));
bundles = bundles.Concat(GetNetCoreBundleKeys(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)));

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

private IEnumerable<RegistryKey> GetNetCoreBundleKeys(RegistryKey uninstallKey)
{
try
{
var uninstalls = uninstallKey
.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");

var names = uninstalls.GetSubKeyNames();

return names
.Select(name => uninstalls.OpenSubKey(name))
.Where(bundle => IsNetCoreBundle(bundle));
}
catch
{
return Enumerable.Empty<RegistryKey>();
}

}

private static bool IsNetCoreBundle(RegistryKey uninstallKey)
{
if (uninstallKey == null)
Expand All @@ -75,7 +77,8 @@ internal static bool IsNetCoreBundle(string displayName, string displayVersion,
((displayName.IndexOf(".NET", StringComparison.OrdinalIgnoreCase) >= 0) ||
(displayName.IndexOf(".NET Runtime", StringComparison.OrdinalIgnoreCase) >= 0) ||
(displayName.IndexOf(".NET SDK", StringComparison.OrdinalIgnoreCase) >= 0) ||
(displayName.IndexOf("Dotnet Shared Framework for Windows Desktop", StringComparison.OrdinalIgnoreCase) >= 0)) &&
(displayName.IndexOf("Dotnet Shared Framework for Windows Desktop", StringComparison.OrdinalIgnoreCase) >= 0) ||
(displayName.IndexOf("Windows Desktop Runtime", StringComparison.OrdinalIgnoreCase) >= 0)) &&
(!String.IsNullOrEmpty(uninstallString)) &&
(uninstallString.IndexOf(".exe", StringComparison.OrdinalIgnoreCase) >= 0) &&
(uninstallString.IndexOf("msiexec", StringComparison.OrdinalIgnoreCase) < 0) &&
Expand All @@ -86,7 +89,7 @@ internal static bool IsNetCoreBundle(string displayName, string displayVersion,
private static Bundle WrapRegistryKey(RegistryKey registryKey)
{
var displayName = registryKey.GetValue("DisplayName") as string;
var uninstallCommand = registryKey.GetValue("QuietUninstallString") as string;
var uninstallCommand = registryKey.GetValue("QuietUninstallString") as string ?? registryKey.GetValue("UninstallString") as string;
var bundleCachePath = registryKey.GetValue("BundleCachePath") as string;

var version = GetBundleVersion(displayName, uninstallCommand, bundleCachePath);
Expand All @@ -103,11 +106,15 @@ private static Bundle WrapRegistryKey(RegistryKey registryKey)
public static BundleVersion GetBundleVersion(string displayName, string uninstallString, string bundleCachePath)
{
var versionString = Regexes.VersionDisplayNameRegex.Match(displayName)?.Value ?? string.Empty;
var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);
var hasAuxVersion = cachePathMatch.Groups[Regexes.AuxVersionGroupName].Success;
var footnote = hasAuxVersion ?
string.Format(LocalizableStrings.HostingBundleFootnoteFormat, displayName, versionString) :
null;
string footnote = null;
if (bundleCachePath != null)
{
var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);
var hasAuxVersion = cachePathMatch.Groups[Regexes.AuxVersionGroupName].Success;
footnote = hasAuxVersion ?
string.Format(LocalizableStrings.HostingBundleFootnoteFormat, displayName, versionString) :
null;
}

try
{
Expand Down Expand Up @@ -146,20 +153,35 @@ private static BundleArch GetBundleArch(string displayName, string bundleCachePa
{
const string x64String = "x64";
const string x86String = "x86";
const string arm64String = "arm64";

var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);

var archString = cachePathMatch.Groups[Regexes.ArchGroupName].Value;
string archString = null;
if (bundleCachePath != null)
{
var cachePathMatch = Regexes.BundleCachePathRegex.Match(bundleCachePath);
archString = cachePathMatch.Groups[Regexes.ArchGroupName].Value;
}

if (string.IsNullOrEmpty(archString))
{
archString = displayName.Contains(x64String) ? x64String : displayName.Contains(x86String) ? x86String : string.Empty;
archString = displayName.Contains(x64String) ?
x64String :
displayName.Contains(x86String) ? x86String : string.Empty;

archString = archString switch
{
string a when a.Contains(x64String) => x64String,
string b when b.Contains(x86String) => x86String,
string b when b.Contains(arm64String) => arm64String,
_ => string.Empty
};
}

switch (archString)
{
case x64String: return BundleArch.X64;
case x86String: return BundleArch.X86;
case arm64String: return BundleArch.Arm64;
case "": return BundleArch.X64 | BundleArch.X86;
default: throw new ArgumentException();
}
Expand Down
14 changes: 14 additions & 0 deletions test/dotnet-core-uninstall.Tests/Shared/BundleInfo/BundleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ public static IEnumerable<object[]> GetDataForTestFrom()
TestUninstallCommand1,
TestDisplayName1
};

yield return new object[]
{
TestHostingBundleVersion1,
BundleArch.Arm64,
TestUninstallCommand1,
TestDisplayName1
};
}

[Theory]
Expand Down Expand Up @@ -186,6 +194,12 @@ public static IEnumerable<object[]> GetDataForTestToString()
TestRuntimeVersion1,
BundleArch.X86
};

yield return new object[]
{
TestRuntimeVersion1,
BundleArch.Arm64
};
}

[Theory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class RegistryQueryTests
[InlineData("Microsoft .NET Core 3.0.0 Preview 6 Build 19307.2 - Windows Server Hosting")]
[InlineData("Microsoft .NET Core 3.1.0 Preview 1 Build preview1.19307.20 - Windows Server Hosting")]
[InlineData("Microsoft .NET 5.0.9 - Windows Server Hosting")]
[InlineData("Microsoft .NET SDK 5.0.100 (arm64)")]
internal void TestIsNetCoreBundleAccept(string input)
{
RegistryQuery.IsNetCoreBundle(input, "0.0", "mockuninstall.exe", "0.0")
Expand Down

0 comments on commit 1680d9d

Please sign in to comment.